
    /* ── Reset & Base ── */

    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    /* Several sticky rows (.dash-tabs, .dash-subnav, .hero-subnav, DM tab
       bars, etc.) intentionally scroll horizontally within themselves on
       mobile via their own overflow-x:auto — but nothing here ever capped
       the outer page itself, so any one of them (or anything else) coming
       in even 1px wider than the viewport — an extra badge, a longer
       label, a role-conditional tab — drags the whole <body> along for
       the ride and leaves it horizontally scrollable, showing blank space
       past the right edge of the real content. Reported as "top buttons
       make the screen longer and leave a blank bar on the right side."
       This is a standard, safe hardening rule: it doesn't affect any of
       the intentional inner overflow-x:auto regions, which still scroll
       exactly as designed — it only stops that overflow from leaking out
       to the page itself. */
    html, body { overflow-x: hidden; max-width: 100vw; }

    :root {

      /* player.html's <meta viewport> opts into viewport-fit=cover
         (edge-to-edge rendering), so on a notched/punch-hole phone the
         topbar and every sticky bar stacked below it (dash-tabs,
         dash-subnav/hero-subnav, vitals-bar, wealth-sticky-summary,
         research-active-training) need this added to their hand-tuned
         top:/height: pixel math, or they render partly under the status
         bar / camera cutout. One shared var so every one of those rules
         references the same value instead of repeating env() by hand.
         Resolves to 0px on devices/browsers without a safe area. */
      --sat: env(safe-area-inset-top, 0px);

      /* Dark purple / greyer-purple background ladder (2026-07-27,
         replaced the near-black/grey originals) — same brightness
         steps as before, hue shifted to purple. bg0/bg1 read as a
         clear dark purple; bg2-bg4 (cards/panels/borders) are a more
         desaturated "greyer purple" so text stays easy to read on them. */
      --bg0:       #130d1c;

      --bg1:       #1a1322;

      --bg2:       #221c2c;

      --bg3:       #2b2434;

      --bg4:       #342c3e;

      --gold:      #ffd700;

      --gold-dim:  #b8960c;

      /* Heraldic scarlet — vibrant red used for critical-state callouts
         and gold/scarlet pairs (heading bars, topbar accent, etc.).
         Distinct from --red (muted danger) so urgent things actually
         pop without lighting up every error message at the same volume. */
      --scarlet:     #e63946;
      --scarlet-dim: #8b1d27;

      --text:      #e8e8f0;

      --text2:     #9898b0;

      --red:       #e05555;

      --green:     #4caf50;

      --blue:      #5b9bd5;

      --purple:    #9b59b6;

      /* Formalized 2026-07-20 — #ff6b35 was already in real use across
         the app (Cost of Living's chart line/points, the Conditions
         button) as an ad-hoc informal "orange," never given a real
         token. Housing/lifestyle-themed cards (Cost of Living, Lifestyle
         Reimbursement) now use it via a real .card-orange class instead
         of repeating the raw hex inline. */
      --orange:    #ff6b35;

      --radius:    10px;

      /* Display face for the wordmark + section headers (guild-charter
         formality); body face for everyday UI text (bureaucratic-courier
         legibility). Loaded via Google Fonts link in each page's <head>. */
      --font-display: 'Cinzel', Georgia, serif;
      --font-body:    'Public Sans', 'Segoe UI', system-ui, sans-serif;

    }

    html, body { height: 100%; font-family: var(--font-body); background: var(--bg0); color: var(--text); }

    @media (prefers-reduced-motion: reduce) {
      .login-card { animation: none !important; }
      .login-logo { animation: none !important; }
      .login-title { animation: none !important; }
    }

    @keyframes rw-card-rise {
      from { opacity: 0; transform: translateY(10px); }
      to   { opacity: 1; transform: translateY(0); }
    }

    /* Login hero — round 2 of the deferred "taller/richer hero" item
       (VISUAL_CHECKLIST.md). Scale + polish of the SAME three existing
       elements (icon/wordmark/subtitle), no new content added — the
       earlier two-column attempt got reverted for creating a second
       competing focal point, so this round deliberately stays within
       "make the one thing bigger and better," not "add more things." */
    @keyframes rw-logo-breathe {
      0%, 100% { filter: drop-shadow(0 0 14px rgba(255,215,0,0.32)); }
      50%      { filter: drop-shadow(0 0 26px rgba(255,215,0,0.52)); }
    }
    @keyframes rw-title-shimmer {
      0%   { background-position: -200% 0; }
      100% { background-position: 200% 0; }
    }

    /* ── Screens ── */

    .screen { display: none; min-height: 100dvh; }

    .screen.active { display: flex; }

    /* ── Login Screen ── */

    #screen-login {

      flex-direction: column;

      align-items: center;

      justify-content: center;

      padding: 2rem 1rem;

      background:
        radial-gradient(ellipse at 50% 0%, rgba(255,215,0,0.07) 0%, transparent 70%),
        repeating-linear-gradient(135deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 46px),
        var(--bg0);

    }

    .login-logo {

      font-size: 4.5rem;

      color: var(--gold);

      margin-bottom: 0.6rem;

      animation: rw-logo-breathe 4s ease-in-out infinite;

    }

    .login-title {

      font-family: var(--font-display);

      font-size: clamp(2.2rem, 8vw, 3.2rem);

      font-weight: 700;

      letter-spacing: 0.16em;

      text-transform: uppercase;

      margin-bottom: 0.4rem;

      background: linear-gradient(100deg, var(--gold) 40%, #fff3cf 50%, var(--gold) 60%);
      background-size: 200% 100%;
      -webkit-background-clip: text;
      background-clip: text;
      -webkit-text-fill-color: transparent;
      animation: rw-title-shimmer 6s linear infinite;

    }

    .login-subtitle {

      font-size: 0.92rem;

      color: var(--text2);

      letter-spacing: 0.22em;

      text-transform: uppercase;

      margin-bottom: 3.2rem;

    }

    .legal-footer {

      margin-top: 2.5rem;

      max-width: 480px;

      text-align: center;

      font-size: 0.7rem;

      color: var(--text2);

      line-height: 1.6;

      opacity: 0.8;

    }

    .legal-footer a {

      color: var(--gold);

      text-decoration: underline;

      cursor: pointer;

      background: none;

      border: none;

      font-size: inherit;

      font-family: inherit;

      padding: 0;

    }

    .legal-footer a:hover { opacity: 0.8; }

    .ogl-modal-body {

      max-height: 60dvh;

      overflow-y: auto;

      font-size: 0.78rem;

      line-height: 1.7;

      color: var(--text1);

      white-space: pre-wrap;

      padding-right: 0.5rem;

    }

    .ogl-modal-body h3 {

      color: var(--gold);

      font-size: 0.85rem;

      margin: 1.2rem 0 0.4rem;

    }

    .login-card {

      background: var(--bg1);

      border: 1px solid rgba(255,215,0,0.18);

      border-radius: 14px;

      padding: 2rem;

      width: 100%;

      max-width: 420px;

      box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset, 0 12px 48px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,215,0,0.04);

      animation: rw-card-rise 0.45s ease-out both;

    }

    #soft-entry-cta.login-card { animation-delay: 0.08s; }

    #new-player-cta.login-card { animation-delay: 0.16s; }

    .auth-wrap {

      flex: 1; display: flex; align-items: flex-start; justify-content: center;

      padding: 2rem 1rem; overflow-y: auto;

    }

    .auth-card {

      background: var(--bg1); border: 1px solid rgba(255,215,0,0.15);

      border-radius: 14px; padding: 2rem; width: 100%;

      box-shadow: 0 8px 40px rgba(0,0,0,0.5);

    }

    .role-tabs {

      display: grid;

      grid-template-columns: 1fr 1fr 1fr;

      gap: 0.5rem;

      margin-bottom: 1.5rem;

    }

    .role-tab {

      padding: 0.75rem;

      border: 1px solid var(--bg4);

      border-radius: 8px;

      background: var(--bg2);

      color: var(--text2);

      cursor: pointer;

      text-align: center;

      font-size: 0.85rem;

      font-weight: 600;

      letter-spacing: 0.05em;

      transition: all 0.15s;

    }

    .role-tab:hover { border-color: var(--gold-dim); color: var(--text); transform: translateY(-1px); box-shadow: 0 4px 14px rgba(255,215,0,0.1); }

    .role-tab.active { background: rgba(255,215,0,0.12); border-color: var(--gold); color: var(--gold); box-shadow: 0 0 0 1px rgba(255,215,0,0.22), 0 4px 16px rgba(255,215,0,0.12); }

    .role-tab i { display: block; font-size: 1.4rem; margin-bottom: 0.35rem; }

    /* Player login sub-mode toggle */

    .player-mode-tabs { display: flex; gap: 0.4rem; margin-bottom: 1.1rem; }

    .ptab {

      flex: 1; padding: 0.45rem 0.5rem; border: 1px solid var(--bg4);

      border-radius: 6px; background: var(--bg2); color: var(--text2);

      cursor: pointer; font-size: 0.78rem; font-weight: 600;

      letter-spacing: 0.04em; text-align: center; transition: all 0.15s;

      font-family: inherit;

    }

    .ptab i { margin-right: 0.3rem; }

    .ptab:hover { border-color: var(--gold-dim); color: var(--text); }

    .ptab.active { background: rgba(255,215,0,0.1); border-color: var(--gold); color: var(--gold); }

    .form-group { margin-bottom: 1rem; }

    .form-group label { display: block; font-size: 0.72rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 0.35rem; }

    .form-input {

      width: 100%; padding: 0.6rem 0.8rem;

      background: var(--bg2); border: 1px solid var(--bg4);

      border-radius: 8px; color: var(--text); font-size: 0.92rem;

      font-family: inherit; transition: border-color 0.15s;

    }

    .form-input:focus { outline: none; border-color: var(--gold); box-shadow: 0 0 0 2px rgba(255,215,0,0.12); }

    .form-input::placeholder { color: var(--bg4); }

    .btn {

      display: inline-flex; align-items: center; gap: 0.4rem;

      padding: 0.6rem 1.2rem; border: none; border-radius: 8px;

      font-family: inherit; font-size: 0.88rem; font-weight: 600;

      cursor: pointer; transition: all 0.15s; text-decoration: none;

    }

    .btn-gold  { background: var(--gold); color: #1a0a00; box-shadow: 0 2px 10px rgba(255,215,0,0.15); }

    .btn-gold:hover { background: #ffe44d; box-shadow: 0 4px 18px rgba(255,215,0,0.3); transform: translateY(-1px); }

    .btn-gold:active { transform: translateY(0); box-shadow: 0 2px 8px rgba(255,215,0,0.2); }

    .btn-blue  { background: var(--blue); color: #fff; }

    .btn-blue:hover { filter: brightness(1.15); }

    /* Default gold instead of neutral grey (2026-07-27) — buttons that
       explicitly set their own border-color/color inline (danger red,
       money green, lore purple, etc.) are untouched: an inline style
       always wins over this class rule, so only genuinely uncolored
       "plain" outline buttons pick up gold from this change. */
    .btn-outline { background: transparent; border: 1px solid var(--gold-dim); color: var(--gold); }

    .btn-outline:hover { border-color: var(--gold); color: var(--gold); box-shadow: 0 0 0 1px rgba(255,215,0,0.15); background: rgba(255,215,0,0.08); }

    .btn-ghost  { background: transparent; border: 1px solid transparent; color: var(--text2); }

    .btn-ghost:hover { color: var(--text); }

    .btn-danger { background: rgba(224,85,85,0.15); border: 1px solid var(--red); color: var(--red); }

    .btn-sm { padding: 0.35rem 0.75rem; font-size: 0.78rem; }

    .btn-full { width: 100%; justify-content: center; }

    .login-error { color: var(--red); font-size: 0.8rem; margin-top: 0.5rem; min-height: 1.2em; }

    .login-help  { font-size: 0.75rem; color: var(--text2); text-align: center; margin-top: 1.25rem; }

    /* ── Wizard Step Indicator ── */

    .wiz-steps-bar { display: flex; align-items: center; justify-content: center; gap: 0; margin-bottom: 1.5rem; }

    .wiz-sdot { width: 30px; height: 30px; border-radius: 50%; border: 2px solid var(--bg4); background: var(--bg2); color: var(--text2); display: flex; align-items: center; justify-content: center; font-size: 0.75rem; font-weight: 700; flex-shrink: 0; transition: all 0.2s; }

    .wiz-sdot.active { border-color: var(--gold); background: rgba(255,215,0,0.15); color: var(--gold); }

    .wiz-sdot.done   { border-color: var(--green); background: rgba(76,175,80,0.12); color: var(--green); }

    .wiz-sline { flex: 1; height: 2px; background: var(--bg4); max-width: 36px; transition: background 0.2s; }

    .wiz-sline.done  { background: var(--green); }

    .wiz-step-label-row { display: flex; justify-content: center; gap: 0.35rem; flex-wrap: wrap; margin-bottom: 1.25rem; }

    .wiz-step-lbl { font-size: 0.68rem; color: var(--text2); text-align: center; min-width: 60px; }

    .wiz-step-lbl.active { color: var(--gold); font-weight: 700; }

    .wiz-body { min-height: 260px; }

    .wiz-nav { display: flex; justify-content: space-between; align-items: center; margin-top: 1.25rem; padding-top: 0.85rem; border-top: 1px solid var(--bg3); }

    .login-help a { color: var(--gold); cursor: pointer; text-decoration: none; }

    .login-help a:hover { text-decoration: underline; }

    .divider { border: none; border-top: 1px solid var(--bg3); margin: 1.25rem 0; }

    /* ── Player: peer-card grids (desktop only) ──
       Several panels are just a single-column stack of same-weight,
       independent cards even on a wide monitor, wasting the 1100px
       .dashboard-body width on one narrow column. These are genuine
       peers (unlike Login's hero/auth split, which competed for the
       same attention) so laying them out side by side on desktop just
       lets them breathe — mobile keeps the exact single-column stack
       it has today, unchanged, since this whole block is gated by the
       media query. ID selectors + .active so this only ever applies to
       the currently-visible panel (matches how switchPlayerTab toggles
       visibility via the .active class, not inline style). */
    @media (min-width: 900px) {
      #ppanel-scheduling.active,
      #ppanel-hr.active,
      #ppanel-world.active {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 1rem;
        align-items: start;
      }

      #ppanel-quest.active {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 1rem;
        align-items: start;
      }
      /* Full-width exceptions: the shift banner, the downtime-activities
         box, and the active-downtime tracker all read better spanning
         the full row rather than being squeezed into one column. */
      #ppanel-quest.active > #p-work-today-card,
      #ppanel-quest.active > .quest-downtime-activities,
      #ppanel-quest.active > #downtime-active-card {
        grid-column: 1 / -1;
      }

      #ppanel-party.active {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 1rem;
        align-items: start;
      }
      /* The friends/chat hub stays full width; only Contact Info / Peer
         Review / NPC Companions become peers. (.party-quick-actions
         moved out of #ppanel-party entirely 2026-07-23, renamed
         .guild-quick-actions — this selector no longer applies to it.) */
      #ppanel-party.active > #party-social-wrap {
        grid-column: 1 / -1;
      }

      /* .card's own margin-bottom would double up with the grid gap
         above — neutralize it only inside these grid panels so vertical
         spacing between rows matches the horizontal gap between columns.
         (#ppanel-hr / #ppanel-world dropped 2026-07-23 — both retired to
         floating modals, no longer exist as elements.) */
      #ppanel-scheduling.active > .card,
      #ppanel-quest.active > .card,
      #ppanel-party.active > .card,
      #ppanel-quest.active > #p-work-today-card,
      #ppanel-quest.active > .quest-downtime-activities,
      #ppanel-party.active > #party-social-wrap {
        margin-bottom: 0;
      }

      /* Hero → Stats: two-column split. Left = "what you're about to do"
         (vitals card, death saves, actions/rest quick-actions), right =
         "what you know" (Proficiencies). Unlike Login's reverted split,
         these aren't competing for the same action — nobody chooses
         between checking HP and checking skills, they're just two
         different modes of reading the same sheet — so side by side
         doesn't fracture focus the way Login's hero/auth columns did.
         No sticky positioning here (kept simple on purpose after the
         Login lesson — can revisit if it turns out to want it). */
      .hero-subpanel[data-hero-sub="stats"] {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 380px);
        gap: 1rem;
        align-items: start;
      }
      /* Left column left on grid auto-flow (only grid-column pinned, no
         explicit grid-row) deliberately — #p-death-saves-card is
         display:none most of the time (only shows at 0 HP). Auto-flow
         means a hidden item generates no grid track at all, so the other
         two items sit flush with a single normal gap between them instead
         of leaving a collapsed-but-still-gapped empty row behind it. */
      .hero-subpanel[data-hero-sub="stats"] > #player-profile-card,
      .hero-subpanel[data-hero-sub="stats"] > #p-death-saves-card,
      .hero-subpanel[data-hero-sub="stats"] > .hero-quick-actions {
        grid-column: 1;
      }
      .hero-subpanel[data-hero-sub="stats"] > .card.card-purple {
        grid-column: 2;
        grid-row: 1 / -1;
      }
      .hero-subpanel[data-hero-sub="stats"] > #player-profile-card,
      .hero-subpanel[data-hero-sub="stats"] > #p-death-saves-card {
        margin-bottom: 0;
      }
    }

    /* ── App Shell (Player & DM dashboards) ── */

    #screen-player, #screen-dm {

      flex-direction: column;

      background: var(--bg0);

    }

    .topbar {

      position: sticky; top: 0; z-index: 100;

      display: flex; align-items: center; gap: 1rem;

      /* viewport-fit=cover (player.html's <meta viewport>) opts into
         edge-to-edge rendering, so on a notched/punch-hole phone the top
         of the page can render under the status bar / camera cutout
         unless something reserves that space. Every other safe-area
         handling in this file (bottom nav, portrait bottom inset) already
         accounts for env(safe-area-inset-bottom) — this was the one
         missing top-side case, and it's what made the topbar look
         "cut off" at the top on some phones. Growing height (not just
         padding) keeps the actual icon row its normal 52px regardless of
         inset size — the extra space is pure buffer above the content,
         not squeezed content. env() resolves to 0 on devices/browsers
         without a safe area, so this is a no-op there. */
      padding: var(--sat) 1.5rem 0;
      height: calc(52px + var(--sat));

      background: var(--bg1); border-bottom: 1px solid rgba(255,215,0,0.1);

      box-shadow: 0 2px 8px -2px rgba(0,0,0,0.4);

    }

    /* Gold → scarlet heraldic accent at the bottom of the topbar.
       Pinned at bottom: 0 (inside the topbar's box) so it can't bleed
       into the first 2 px of the tab bar — the previous bottom: -2px
       made the gradient draw on top of the tab strip's leading edge,
       which read like a hairline gap between the two banners. */
    .topbar::after {

      content: ''; position: absolute; left: 0; right: 0; bottom: 0;

      height: 2px; pointer-events: none;

      background: linear-gradient(90deg, transparent 0%, var(--gold) 18%, var(--scarlet) 50%, var(--gold) 82%, transparent 100%);

      opacity: 0.55;

    }

    .topbar-logo { font-family: var(--font-display); font-size: 1.05rem; font-weight: 700; letter-spacing: 0.08em; color: var(--gold); text-transform: uppercase; }

    .topbar-role { font-size: 0.7rem; color: var(--text2); border: 1px solid var(--bg4); border-radius: 20px; padding: 0.15rem 0.55rem; letter-spacing: 0.08em; text-transform: uppercase; }

    /* Character switcher button — replaces the static .topbar-role.
       Pill with an avatar icon, vertically-stacked char name + sub
       line (player name on mobile, campaign on desktop), and an
       always-visible gold chevron so the dropdown affordance is
       obvious. The button takes flex:1 inside the topbar so it
       grows to fill the empty space between the logo and the
       right-side icons rather than crowding into a fixed pill. */
    #char-switcher-btn {
      /* Default to visible so the switcher renders the moment the
         player dashboard mounts. Previous default was display:none
         on the assumption that JS would always flip it to flex —
         when refreshCharSwitcher errored or never reached the
         display:flex line, the button stayed invisible and the
         topbar showed an empty void between the logo and the
         right-side icons (the reported mobile bug). The button
         only exists inside #screen-player, so being visible by
         default doesn't bleed into login / no-character screens.
         JS still flips it to display:none on logout / no-profile. */
      display: flex;
      align-items: center;
      gap: 0.4rem;
      background: var(--bg2);
      border: 1px solid var(--bg3);
      border-radius: 18px;
      padding: 0.3rem 0.7rem 0.3rem 0.55rem;
      cursor: pointer;
      font: inherit;
      color: var(--text);
      /* Sized to content. Originally tried flex:99 to stretch the
         button to fill the topbar's free space — but with
         left-aligned text inside, that left an awkward void
         between the text and the chevron. The natural layout
         (compact pill hugging the logo, spacer takes the empty
         space between switcher and right icons) reads better. */
      flex: 0 0 auto;
      min-width: 0;
      max-width: 360px;
      -webkit-tap-highlight-color: transparent;
      transition: background 0.1s, border-color 0.1s;
    }
    #char-switcher-btn:hover { background: var(--bg3); border-color: rgba(255,215,0,0.35); }
    #char-switcher-btn:active { transform: scale(0.99); }
    #char-switcher-btn .csb-text {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      line-height: 1.1;
      min-width: 0; flex: 1;
    }
    #char-switcher-btn .csb-name {
      font-size: 0.82rem;
      font-weight: 700;
      color: var(--gold);
      letter-spacing: 0.02em;
      max-width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    #char-switcher-btn .csb-sub {
      font-size: 0.62rem;
      color: var(--text2);
      font-weight: 500;
      letter-spacing: 0.02em;
      max-width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    @media (max-width: 600px) {
      /* Hide the "RealmWatch" wordmark on phones — keep just the
         dungeon icon. Frees ~100px of horizontal space for the
         switcher button to fill. */
      .topbar-logo-text { display: none; }
      /* Tighten the topbar's gap + padding so the switcher gets
         every pixel possible. */
      .topbar { gap: 0.35rem; padding: 0 0.45rem; }
      /* Switcher sits right next to the logo on the left — the
         spacer (flex:1, default) eats the empty space between the
         switcher and the right-side icons, pushing those icons to
         the right edge. No auto-margin centering: the user wants
         the switcher anchored beside the logo. */
      #char-switcher-btn {
        max-width: 220px;
        padding: 0.4rem 0.55rem 0.4rem 0.45rem;
        gap: 0.3rem;
      }
      #char-switcher-btn .csb-name { font-size: 0.82rem; }
      #char-switcher-btn .csb-sub  { font-size: 0.6rem; }
      /* Slim right-side icons. Default btn-sm padding eats ~16px
         per side with gaps; bringing them down to a tighter
         footprint frees ~40-60px combined for the switcher to
         actually show the character name without ellipsing it
         to "Sy...". Inline styles on individual buttons override
         padding rules; use !important sparingly here so the
         per-button color/badge styling stays intact. */
      .topbar > .btn.btn-ghost.btn-sm { padding: 0.28rem 0.4rem !important; }
      .topbar > .rwhub-mail-btn       { font-size: 0.95rem !important; padding: 0.28rem 0.45rem !important; }
      /* Help icon (spider search). Restored on mobile — the index
         now spiders the player's inventory / spellbook / contacts /
         factions / quests, not just static tab shortcuts, so it's
         the fastest way to jump to a thing by name on a small
         screen. Slim padding inherited from the .btn-ghost.btn-sm
         rule above. */
      .topbar-help-btn { font-size: 0.95rem; }
    }

    .topbar-spacer { flex: 1; }

    .topbar-user { font-size: 0.82rem; color: var(--text2); }

    .topbar-user strong { color: var(--text); }

    .dashboard-body { flex: 1; padding: 0 1.5rem calc(1.5rem + env(safe-area-inset-bottom, 0)); max-width: 1100px; margin: 0 auto; width: 100%; }
    .dash-panel:not(:has(> .hero-subnav)) { padding-top: 1.5rem; }

    .page-title { font-family: var(--font-display); font-size: 1.3rem; font-weight: 700; color: var(--gold); margin-bottom: 1.5rem; }

    /* ── Cards ── */

    .card {

      background: var(--bg1); border: 1px solid var(--bg3);

      border-radius: var(--radius); padding: 1.25rem;

      margin-bottom: 1rem;

      /* Light touch on purpose — this rule repeats dozens of times per
         screen, so it only needs to take the edge off pure-flat, not
         read as a raised hero element the way .login-card does. */
      box-shadow: 0 4px 14px -10px rgba(0,0,0,0.6);

    }

    .card-gold { border-color: rgba(255,215,0,0.35); box-shadow: 0 0 0 1px rgba(255,215,0,0.05) inset, 0 8px 18px -16px rgba(255,215,0,0.35); }

    /* Blue/green/purple card variants — same recipe as .card-gold/.card-scarlet
       (tinted border, subtle inset+drop shadow, colored heading bar). Used to
       give visually-identical stacked cards (e.g. Scheduling, HR) a distinct,
       semantically-meaningful accent instead of one flat repeated gray box. */
    .card-blue   { border-color: rgba(91,155,213,0.35);  box-shadow: 0 0 0 1px rgba(91,155,213,0.05) inset, 0 8px 18px -16px rgba(91,155,213,0.4); }
    .card-green  { border-color: rgba(76,175,80,0.35);   box-shadow: 0 0 0 1px rgba(76,175,80,0.05) inset, 0 8px 18px -16px rgba(76,175,80,0.4); }
    .card-purple { border-color: rgba(155,89,182,0.35);  box-shadow: 0 0 0 1px rgba(155,89,182,0.05) inset, 0 8px 18px -16px rgba(155,89,182,0.4); }
    /* Orange variant (2026-07-20) — see the --orange token comment above
       for why this exists as a real class now. */
    .card-orange { border-color: rgba(255,107,53,0.35);  box-shadow: 0 0 0 1px rgba(255,107,53,0.05) inset, 0 8px 18px -16px rgba(255,107,53,0.4); }

    .card-blue h3   { color: var(--blue);   border-left-color: var(--blue);   text-shadow: 0 0 8px rgba(91,155,213,0.22); }
    .card-green h3  { color: var(--green);  border-left-color: var(--green);  text-shadow: 0 0 8px rgba(76,175,80,0.22); }
    .card-purple h3 { color: var(--purple); border-left-color: var(--purple); text-shadow: 0 0 8px rgba(155,89,182,0.22); }
    .card-orange h3 { color: var(--orange); border-left-color: var(--orange); text-shadow: 0 0 8px rgba(255,107,53,0.22); }

    /* Forge's "no proficiency" locked-station message — was a bare inline
       style repeated ~17 times (one per crafting station). One shared class
       so it reads as an intentional "locked toolbox slot" card instead of
       plain centered text floating on the background, and so any future
       tweak only needs to happen once. */
    .noprof-msg {
      text-align: center; padding: 2.25rem 1.25rem; color: var(--text2);
      font-size: 0.8rem; line-height: 1.5;
      background: var(--bg2); border: 1px dashed var(--bg4); border-radius: 10px;
    }
    .noprof-msg i { margin-bottom: 0.75rem !important; }

    /* Vivarium's "bare row card" — the same flat inline recipe
       (bg2/bg3/8px/~0.6rem padding, no accent) was repeated verbatim
       across Your Reports, Storage items, Pet/Plant Shop cards, and both
       DM admin lists (Species Library, Capture Requests) — same
       "repeats N times, one class fixes all of them" precedent as
       .noprof-msg above. Base class carries the shared shape; a
       per-context left-accent modifier (matching the existing
       var(--blue)/--green/--purple/--gold tokens) gives each list its
       own identity instead of one undifferentiated gray box repeated
       everywhere. */
    .viv-row-card {
      display: flex; align-items: center; gap: 0.6rem;
      background: var(--bg2); border: 1px solid var(--bg3); border-left: 3px solid var(--bg4);
      border-radius: 8px; padding: 0.6rem 0.75rem; margin-bottom: 0.4rem;
    }
    .viv-row-card--blue   { border-left-color: var(--blue); }
    .viv-row-card--green  { border-left-color: var(--green); }
    .viv-row-card--purple { border-left-color: var(--purple); }
    .viv-row-card--gold   { border-left-color: var(--gold); }

    /* Vivarium's section micro-label ("Your Reports", "Give Care",
       "Suspects", "Habitat Items", etc.) — same uppercase/tracked-out
       label repeated across the feature with only tiny, likely-accidental
       variations (some bold, some not; 0.68-0.72rem). Standardizes on the
       majority style (bold, 0.7rem) rather than preserving that
       inconsistency; per-site margin overrides stay inline where a
       specific spot genuinely needs a different one. */
    .viv-section-label {
      font-size: 0.7rem; font-weight: 700; color: var(--text2);
      text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.4rem;
    }

    /* ── Shop item cards — same skeleton (icon/title/meta/description,
       divider, price+actions row) across all 4 Shop tabs, but each tab
       gets its own texture so they still feel like different places.
       Only the outer card container + action row use these classes —
       the info content inside (item stats, descriptions, etc.) is
       untouched so nothing that already worked changes shape. */
    .shop-card { border-radius: 8px; padding: 0.7rem 0.85rem; margin-bottom: 0.5rem; position: relative; }
    .shop-card-actions {
      margin-top: 0.45rem; padding-top: 0.4rem; border-top: 1px solid var(--bg3);
      display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap;
    }

    /* Dashercoin — warm gold-foil sheen, like a coin catalog page */
    .shop-card--catalog {
      background: linear-gradient(135deg, rgba(255,215,0,0.10) 0%, rgba(255,215,0,0.02) 45%, var(--bg2) 100%);
      border: 1px solid rgba(255,215,0,0.3);
    }

    /* Online — shipping-label corner fold + dashed edge */
    .shop-card--online {
      background: var(--bg2);
      border: 1px dashed rgba(255,153,0,0.35);
      clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 0 100%);
    }

    /* Company Store — stamped requisition slip: double-ruled top/bottom */
    .shop-card--merchant {
      background: var(--bg2);
      border-top: 2px solid rgba(76,175,80,0.45);
      border-bottom: 2px solid rgba(76,175,80,0.45);
      border-left: 1px solid rgba(76,175,80,0.2);
      border-right: 1px solid rgba(76,175,80,0.2);
    }

    /* Components — faceted crystal, cut corners */
    .shop-card--components {
      background: linear-gradient(160deg, rgba(180,130,255,0.09), var(--bg2));
      border: 1px solid rgba(180,130,255,0.3);
      clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
    }

    /* Grocery — fresh produce-crate green, dashed like a farm-stand tag */
    .shop-card--grocery {
      background: linear-gradient(135deg, rgba(112,200,64,0.09) 0%, rgba(112,200,64,0.02) 45%, var(--bg2) 100%);
      border: 1px dashed rgba(112,200,64,0.35);
    }

    /* Scarlet high-priority card — for urgent/danger sections (Initiative
       Tracker, HP critical alerts, etc.). Brighter than --red so the
       "this needs attention" cards out-shout normal cards without
       slamming the whole UI with hot color. */
    .card-scarlet { border-color: rgba(230,57,70,0.4); box-shadow: 0 0 0 1px rgba(230,57,70,0.06) inset, 0 8px 18px -16px rgba(230,57,70,0.4); background: linear-gradient(135deg, rgba(230,57,70,0.04), transparent), var(--bg1); }

    /* Heading flag-bar: a small gold tick mark on the left edge of every
       card heading, plus a faint gold underline glow. Reads as a
       heraldic banner without crowding out the heading text. */
    .card h3 { font-size: 0.95rem; color: var(--gold); margin-bottom: 0.75rem; display: flex; align-items: center; gap: 0.5rem; padding-left: 0.6rem; border-left: 3px solid var(--gold); line-height: 1.3; text-shadow: 0 0 8px rgba(255,215,0,0.18); }

    /* Scarlet variant for the same heading bar — used by .card-scarlet
       sections so the bar matches the card's accent color. */
    .card-scarlet h3 { color: var(--scarlet); border-left-color: var(--scarlet); text-shadow: 0 0 8px rgba(230,57,70,0.22); }

    /* ── Stat Grid ── */

    .stat-row { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1rem; }

    .stat-box {

      flex: 1; min-width: 70px;

      background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px;

      padding: 0.5rem 0.4rem; text-align: center;

    }

    .stat-box .stat-label { font-size: 0.58rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.1em; }

    .stat-box .stat-value { font-size: 1.1rem; font-weight: 700; color: var(--gold); }

    .stat-box .stat-mod   { font-size: 0.65rem; color: var(--text2); }

    /* Ability score grid: explicit 3 columns × 2 rows. .stat-row is
       flex-wrap which broke into 2/2/2 or 3/3 depending on width;
       fixing the grid keeps the six classic abilities in a clean
       block on every screen size. */
    #p-ability-scores { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-bottom: 1rem; }
    #p-ability-scores .stat-box { min-width: 0; flex: unset; border-left-width: 3px; border-left-style: solid; }

    /* Per-ability accent stripe + matching label/value colors so the
       text follows the color coding instead of every box reading
       gold. Common D&D-fanart palette: STR=scarlet, DEX=green,
       CON=orange, INT=blue, WIS=purple, CHA=gold. */
    #p-ability-scores .stat-box[data-ab="str"] { border-left-color: var(--scarlet); }
    #p-ability-scores .stat-box[data-ab="dex"] { border-left-color: #4caf50; }
    #p-ability-scores .stat-box[data-ab="con"] { border-left-color: #e89c44; }
    #p-ability-scores .stat-box[data-ab="int"] { border-left-color: var(--blue); }
    #p-ability-scores .stat-box[data-ab="wis"] { border-left-color: var(--purple); }
    #p-ability-scores .stat-box[data-ab="cha"] { border-left-color: var(--gold); }
    #p-ability-scores .stat-box[data-ab="str"] .stat-value,
    #p-ability-scores .stat-box[data-ab="str"] .stat-label { color: var(--scarlet); }
    #p-ability-scores .stat-box[data-ab="dex"] .stat-value,
    #p-ability-scores .stat-box[data-ab="dex"] .stat-label { color: #4caf50; }
    #p-ability-scores .stat-box[data-ab="con"] .stat-value,
    #p-ability-scores .stat-box[data-ab="con"] .stat-label { color: #e89c44; }
    #p-ability-scores .stat-box[data-ab="int"] .stat-value,
    #p-ability-scores .stat-box[data-ab="int"] .stat-label { color: var(--blue); }
    #p-ability-scores .stat-box[data-ab="wis"] .stat-value,
    #p-ability-scores .stat-box[data-ab="wis"] .stat-label { color: var(--purple); }
    #p-ability-scores .stat-box[data-ab="cha"] .stat-value,
    #p-ability-scores .stat-box[data-ab="cha"] .stat-label { color: var(--gold); }
    /* Combat stats (AC/Init/Speed/Prof) moved onto the identity row in the
       vitals bar 2026-07-19, sized up a bit since they're standalone there
       rather than packed in a dense grid — scoped to #p-combat-stats
       specifically so the Abilities score grid (same .stat-box class,
       shares the base rule above) is untouched.
       flex-grow bumped 0→1 (2026-07-24): this row grew from the original
       4 tiles (AC/Init/Speed/Prof) to 7 (added Weapon Atk/Spell Atk/Save
       DC), and on narrow mobile widths that wraps 3-per-line with a lone
       7th tile on its own row — with grow:0 that tile stayed its natural
       content width, leaving a wide dead strip of empty background next
       to it. Growing lets each wrapped line's tiles fill 100% of that
       line's width, whatever the count, instead of leaving a gap. */
    /* Fluid (clamp()) 2026-07-25 — scales the 7 tiles' size continuously
       with viewport width instead of one fixed size for every screen
       above the 640px mobile breakpoint. Scoped to #p-combat-stats only,
       so #p-ability-scores' shared .stat-box base rule is untouched.
       Sizes trimmed further 2026-07-26 — Save DC (the 7th tile) was
       wrapping onto its own full-width row since the first 6 already
       filled the available line at the old sizes; shrinking each tile a
       little lets all 7 sit on one row instead. */
    #p-combat-stats.stat-row { gap: clamp(0.25rem, 0.2rem + 0.25vw, 0.4rem); margin-bottom: 0; }
    #p-combat-stats .stat-box { flex: 1 0 auto; min-width: clamp(44px, 38px + 2vw, 60px); padding: clamp(0.3rem, 0.25rem + 0.2vw, 0.42rem) clamp(0.3rem, 0.25rem + 0.3vw, 0.5rem); }
    #p-combat-stats .stat-box .stat-label { font-size: clamp(0.52rem, 0.48rem + 0.1vw, 0.62rem); }
    #p-combat-stats .stat-box .stat-value { font-size: clamp(0.95rem, 0.85rem + 0.35vw, 1.2rem); }
    #p-combat-stats .stat-box .stat-mod   { font-size: clamp(0.56rem, 0.52rem + 0.1vw, 0.66rem); }
    /* Modifier coloring — positive green, negative scarlet, zero stays
       muted. Uses :where so specificity stays low for the active
       gold-on-positive override below. */
    .stat-box .stat-mod.pos { color: #4caf50; font-weight: 600; }
    .stat-box .stat-mod.neg { color: var(--scarlet); font-weight: 600; }

    .info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }

    @media (max-width: 500px) { .info-grid { grid-template-columns: 1fr; } }

    .info-row { display: flex; justify-content: space-between; align-items: center; font-size: 0.83rem; padding: 0.3rem 0; border-bottom: 1px solid var(--bg3); }

    .info-row:last-child { border-bottom: none; }

    .info-row .label { color: var(--text2); }

    .info-row .value { color: var(--text); font-weight: 600; }

    /* ── Finance Cards ── */

    .finance-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75rem; margin-bottom: 1rem; }

    @media (max-width: 500px) { .finance-grid { grid-template-columns: 1fr; } }

    .finance-box { background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px; padding: 0.75rem; text-align: center; }

    .finance-box .f-label { font-size: 0.65rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 0.25rem; }

    .finance-box .f-value { font-size: 1.25rem; font-weight: 700; }

    .f-debt { color: var(--red); }

    .f-earn { color: var(--green); }

    .f-net  { color: var(--gold); }

    /* ── Sessions list ── */

    .session-item {

      display: flex; align-items: flex-start; gap: 0.75rem;

      padding: 0.75rem 0; border-bottom: 1px solid var(--bg3);

    }

    .session-item:last-child { border-bottom: none; }

    .session-date-box {

      background: var(--bg2); border-radius: 8px; padding: 0.4rem 0.6rem;

      text-align: center; min-width: 46px; flex-shrink: 0;

    }

    .session-date-box .s-month { font-size: 0.6rem; text-transform: uppercase; color: var(--text2); }

    .session-date-box .s-day   { font-size: 1.1rem; font-weight: 700; color: var(--gold); line-height: 1; }

    .session-info { flex: 1; }

    .session-name { font-weight: 600; font-size: 0.9rem; margin-bottom: 0.15rem; }

    .session-meta { font-size: 0.75rem; color: var(--text2); }

    .badge {

      display: inline-block; padding: 0.15rem 0.5rem; border-radius: 20px;

      font-size: 0.68rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;

    }

    .badge-green  { background: rgba(76,175,80,0.15);  color: var(--green); border: 1px solid rgba(76,175,80,0.3); }

    .badge-gold   { background: rgba(255,215,0,0.12);  color: var(--gold);  border: 1px solid rgba(255,215,0,0.3); }

    .badge-gray   { background: rgba(150,150,180,0.1); color: var(--text2); border: 1px solid var(--bg4); }

    .badge-blue   { background: rgba(91,155,213,0.15); color: var(--blue);  border: 1px solid rgba(91,155,213,0.3); }

    .badge-red    { background: rgba(224,85,85,0.15);  color: var(--red);   border: 1px solid rgba(224,85,85,0.3); }

    /* Legacy v1 badge tiers */
    .badge-bronze { background: rgba(205,127,50,0.15);  color: #cd7f32; border: 1px solid rgba(205,127,50,0.35); }
    .badge-silver { background: rgba(192,192,192,0.15); color: #c0c0c0; border: 1px solid rgba(192,192,192,0.35); }
    .badge-purple { background: rgba(180,140,255,0.15); color: var(--purple); border: 1px solid rgba(180,140,255,0.35); }

    /* ── Player Table (DM view) ── */

    .table-wrap { overflow-x: auto; }

    table { width: 100%; border-collapse: collapse; font-size: 0.83rem; }

    th { text-align: left; padding: 0.5rem 0.75rem; color: var(--text2); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.08em; border-bottom: 1px solid var(--bg3); }

    td { padding: 0.55rem 0.75rem; border-bottom: 1px solid var(--bg2); vertical-align: middle; }

    tr:last-child td { border-bottom: none; }

    tr:hover td { background: var(--bg2); }

    /* ── Stats Overview (DM) ── */

    .kpi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.75rem; margin-bottom: 1.5rem; }

    @media (max-width: 640px) { .kpi-grid { grid-template-columns: 1fr 1fr; } }

    .kpi-box { background: var(--bg1); border: 1px solid var(--bg3); border-radius: 10px; padding: 1rem; text-align: center; }

    .kpi-box .kpi-value { font-size: 1.8rem; font-weight: 800; color: var(--gold); }

    .kpi-box .kpi-label { font-size: 0.68rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.1em; margin-top: 0.2rem; }

    /* ── Activity Feed ── */

    .activity-item { display: flex; gap: 0.6rem; align-items: flex-start; padding: 0.45rem 0; border-bottom: 1px solid var(--bg2); font-size: 0.8rem; }

    .activity-item:last-child { border-bottom: none; }

    .activity-icon { font-size: 1rem; flex-shrink: 0; margin-top: 0.05rem; }

    .activity-text { color: var(--text); flex: 1; }

    .activity-time { color: var(--text2); font-size: 0.7rem; flex-shrink: 0; }

    /* ── DM Reg Modal ── */

    .modal-backdrop {

      position: fixed; inset: 0; background: rgba(0,0,0,0.75);

      z-index: 9999; display: none; align-items: center; justify-content: center; padding: 1rem;

    }

    /* The inbox is itself a modal-backdrop, and dialogs are launched from
       inside it (e.g. the Session Wizard / session edit via the inbox's
       Sessions tab). Sit the inbox one layer below the standard modal
       z-index so any modal opened on top of it actually renders above it,
       instead of behind it. Still above all non-modal page content. */
    #modal-inbox, #modal-player-inbox { z-index: 9990; }

    /* Vivarium minigames (Compost/Play/Tend) are launched from a button
       inside the instance modal (#viv-instance-dlg), which is dynamically
       created and appended to <body> on open — always last in the DOM,
       so at the same default z-index it would stack on top of these
       static modals regardless of open order. Bump above it explicitly,
       same fix pattern as the inbox exception above but in the opposite
       direction (these need to win the stacking tie, not lose it). */
    #modal-compost, #modal-play-pet, #modal-tend-plant { z-index: 10000; }

    .modal-backdrop.open { display: flex; }

    .modal {

      background: var(--bg1); border: 1px solid rgba(255,215,0,0.2);

      border-radius: 14px; padding: 1.75rem; width: 100%; max-width: 420px;

      box-shadow: 0 12px 60px rgba(0,0,0,0.6);

      /* Tall modals (multiclass level-up wizard, etc) need to scroll
         when their content exceeds the viewport. The backdrop has
         1rem padding on each side, so subtract 2rem from 100dvh.
         dvh (dynamic viewport height) shrinks when the mobile browser
         chrome (address bar, tab bar) appears, keeping the modal
         fully visible without content hiding under the chrome. */

      max-height: calc(100dvh - 2rem);

      overflow-y: auto;

      overscroll-behavior: contain;

      /* Reserves the scrollbar's width permanently instead of only when
         content overflows — without this, a minigame whose content height
         changes mid-play (canvas resize, status bar wrapping, etc.) toggles
         the scrollbar on/off, and each toggle shifts the modal's content
         width sideways ("screen moves during minigames"). */
      scrollbar-gutter: stable;

    }

    .modal h2 { font-size: 1.1rem; color: var(--gold); margin-bottom: 0.25rem; }

    .modal p  { font-size: 0.8rem; color: var(--text2); margin-bottom: 1rem; }

    .modal-footer { display: flex; gap: 0.5rem; justify-content: flex-end; margin-top: 1.25rem; }

    /* ── Encounter Builder modal ── responsive layout + tap targets */
    .enc-bldr-grid     { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; flex: 1; min-height: 0; }
    .enc-bldr-pane     { display: flex; flex-direction: column; background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px; padding: 0.5rem; min-height: 0; }
    .enc-bldr-filters  { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-bottom: 0.45rem; align-items: center; }
    .enc-bldr-filters .form-input { font-size: 0.8rem; padding: 0.3rem 0.45rem; }
    .enc-bldr-filters .filter-q   { flex: 1; min-width: 140px; }
    .enc-bldr-filters .filter-cr  { width: 96px; }
    .enc-bldr-filters .filter-type{ min-width: 130px; flex: 1; }
    .enc-bldr-row      { display: grid; grid-template-columns: 1fr auto auto auto; gap: 0.4rem; align-items: center; padding: 0.4rem 0.4rem; border-bottom: 1px solid rgba(255,255,255,0.04); }
    .enc-bldr-row .name-col   { min-width: 0; }
    .enc-bldr-row .name-line  { font-size: 0.85rem; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .enc-bldr-row .meta-line  { font-size: 0.66rem; color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .enc-bldr-row .stat-col   { font-size: 0.7rem; color: var(--text2); text-align: right; white-space: nowrap; line-height: 1.15; }
    .enc-bldr-row .stat-col .sub { font-size: 0.62rem; }
    .enc-bldr-tap     { min-width: 38px; min-height: 38px; padding: 0.32rem 0.55rem; display: inline-flex; align-items: center; justify-content: center; }
    .enc-bldr-count   { width: 56px; min-height: 38px; font-size: 0.92rem; text-align: center; padding: 0.25rem; background: var(--bg2); border: 1px solid var(--bg3); border-radius: 4px; color: var(--text); }
    .enc-lib-card     { padding: 0.65rem; display: flex; justify-content: space-between; gap: 0.5rem; align-items: flex-start; }
    .enc-lib-actions  { display: flex; flex-direction: column; gap: 0.3rem; flex-shrink: 0; }
    .enc-lib-actions .btn { min-height: 36px; }

    @media (max-width: 720px) {
      /* Stack the two-pane builder vertically; cap catalog so the encounter pane stays visible. */
      .enc-bldr-grid    { grid-template-columns: 1fr; }
      .enc-bldr-pane.catalog  { max-height: 42dvh; }
    }
    @media (max-width: 480px) {
      .enc-bldr-filters .filter-q,
      .enc-bldr-filters .filter-cr,
      .enc-bldr-filters .filter-type { flex: 1 1 100%; min-width: 0; width: auto; }
      .enc-bldr-row     { grid-template-columns: 1fr auto auto; gap: 0.3rem; }
      .enc-bldr-row .stat-col.hp-ac { display: none; }   /* drop HP/AC on the catalog row to keep the + button reachable */
      .enc-lib-card     { flex-direction: column; }
      .enc-lib-actions  { flex-direction: row; flex-wrap: wrap; width: 100%; }
      .enc-lib-actions .btn { flex: 1 1 calc(50% - 0.15rem); }
    }

    /* ── DM Registration Wizard ── */

    .dmwiz-step-dot { color: var(--text2); background: var(--bg2); border-right: 1px solid var(--bg3); }

    .dmwiz-step-dot:last-child { border-right: none; }

    .dmwiz-step-dot.active { background: rgba(255,215,0,0.18); color: var(--gold); }

    .dmwiz-step-dot.done   { background: rgba(76,175,80,0.12);  color: var(--green); }

    /* ── Inbox modal ── */

    /* dvh = dynamic viewport height; shrinks when the on-screen
       keyboard opens so the compose bar stays visible. vh fallback
       for browsers without dvh support (pre-2023) — those still get
       the previous behavior (input may sit under keyboard). */
    .inbox-modal { max-width: 680px !important; max-height: 82vh !important; max-height: 82dvh !important; padding: 0 !important; display: flex !important; flex-direction: column; overflow: hidden; }

    .inbox-panel { display: flex; flex: 1; min-height: 0; overflow: hidden; }

    .inbox-thread-list { width: 200px; flex-shrink: 0; border-right: 1px solid var(--bg3); overflow-y: auto; padding: 0.5rem; background: var(--bg0); }

    .inbox-thread-item { padding: 0.5rem 0.6rem; border-radius: 7px; cursor: pointer; margin-bottom: 0.25rem; transition: background 0.1s; }

    .inbox-thread-item:hover { background: var(--bg3); }

    .inbox-thread-item.active { background: rgba(255,215,0,0.1); border: 1px solid rgba(255,215,0,0.2); }

    /* .inbox-view is itself a flex-column with .inbox-messages inside.
       Both min-width:0 AND min-height:0 are needed: the panel above
       this is row-direction on desktop (min-width matters, prevents
       horizontal overflow squeezing) and column-direction on mobile
       (min-height matters, lets this view shrink below its content
       size so the inner overflow-y:auto on .inbox-messages can engage).
       Forgetting min-height:0 here was the real reason mobile scroll
       didn't work — the height-zero fix on .inbox-messages was
       inert because this parent grew to fit content anyway. */
    .inbox-view {
      flex: 1;
      min-width: 0;
      min-height: 0;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }

    /* min-height:0 is the magic line for flex-column scroll containers
       — without it, the default min-height:auto refuses to shrink the
       container below its content size, which means flex:1 doesn't
       actually constrain the height and overflow-y:auto never engages.
       -webkit-overflow-scrolling:touch enables smooth momentum scroll
       on older iOS. touch-action:pan-y tells the browser explicitly
       that this element handles vertical pans (prevents iOS from
       trying to capture the touch for some other gesture, like
       rubber-band on the modal backdrop). overscroll-behavior:contain
       stops the scroll from chaining to parent elements when you hit
       the top/bottom. */
    .inbox-messages {
      flex: 1; min-height: 0;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      touch-action: pan-y;
      overscroll-behavior: contain;
      padding: 0.75rem;
      display: flex; flex-direction: column; gap: 0.4rem;
    }

    .inbox-msg { max-width: 78%; padding: 0.55rem 0.9rem; border-radius: 14px; font-size: 0.82rem; word-break: break-word; line-height: 1.45; }

    .inbox-msg.mine   { align-self: flex-end;  border-radius: 14px 4px 14px 14px;
                        background: linear-gradient(135deg, rgba(212,175,55,0.28), rgba(180,130,20,0.1));
                        border: 1px solid rgba(212,175,55,0.45); }

    .inbox-msg.theirs { align-self: flex-start; border-radius: 4px 14px 14px 14px;
                        background: linear-gradient(135deg, rgba(90,100,220,0.18), rgba(59,100,200,0.07));
                        border: 1px solid rgba(99,102,241,0.3);
                        border-left: 3px solid rgba(99,102,241,0.55); }

    .inbox-compose-bar { border-top: 1px solid var(--bg3); padding: 0.6rem 0.75rem; display: flex; gap: 0.5rem; flex-shrink: 0; }

    /* ── Party Chat Card (pinned group chat button) ────────────── */
    #party-chat-card {
      display:flex; align-items:center; gap:0.6rem;
      padding:0.5rem 0.65rem; margin:0.4rem 0.4rem 0;
      background:linear-gradient(135deg,rgba(255,215,0,0.1),rgba(255,215,0,0.04));
      border:1px solid rgba(255,215,0,0.3); border-radius:9px;
      cursor:pointer; transition:background 0.12s, border-color 0.12s;
      flex-shrink:0;
    }
    #party-chat-card:hover { background:rgba(255,215,0,0.16); border-color:var(--gold); }
    #party-chat-card.active { background:rgba(255,215,0,0.18); border-color:var(--gold); box-shadow:0 0 0 1px rgba(255,215,0,0.2); }
    #party-chat-card .pc-icon { width:32px;height:32px;border-radius:50%;background:rgba(255,215,0,0.15);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:0.9rem;color:var(--gold); }
    #party-chat-card .pc-label { flex:1;min-width:0; }
    #party-chat-card .pc-name { font-size:0.8rem;font-weight:700;color:var(--text); }
    #party-chat-card .pc-sub  { font-size:0.62rem;color:var(--text2); }
    #party-chat-unread { background:var(--scarlet);color:#fff;border-radius:999px;padding:0.05rem 0.38rem;font-size:0.6rem;font-weight:700;display:none; }
    /* Group chat bubble — wider than DM, shows sender name above */
    .inbox-msg.group { max-width:88%; }
    .inbox-msg.group .msg-sender-row { display:flex; align-items:center; gap:0.35rem; margin-bottom:0.2rem; cursor:pointer; width:fit-content; }
    .inbox-msg.group .msg-sender-row:hover .msg-sender-name { text-decoration:underline; }
    .msg-sender-avatar { width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:0.5rem;flex-shrink:0; }
    .msg-sender-name { font-size:0.65rem;font-weight:700;color:var(--text2); }
    /* Name-click popover */
    #player-name-popover {
      position:fixed; z-index:10000;
      background:var(--bg2); border:1px solid var(--bg3); border-radius:10px;
      box-shadow:0 8px 32px rgba(0,0,0,0.45); padding:0.5rem;
      min-width:160px; display:none; flex-direction:column; gap:0.2rem;
    }
    #player-name-popover.open { display:flex; }
    .popover-header { display:flex; align-items:center; gap:0.5rem; padding:0.35rem 0.5rem 0.55rem; border-bottom:1px solid var(--bg3); margin-bottom:0.2rem; }
    .popover-avatar { width:30px;height:30px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:0.75rem;flex-shrink:0; }
    .popover-name   { font-size:0.82rem;font-weight:700;color:var(--text); }
    .popover-class  { font-size:0.63rem;color:var(--text2); }
    .popover-btn    { display:flex;align-items:center;gap:0.5rem;padding:0.38rem 0.55rem;border-radius:7px;font-size:0.78rem;color:var(--text);cursor:pointer;transition:background 0.1s;border:none;background:none;width:100%;text-align:left; }
    .popover-btn:hover { background:var(--bg3); }
    .popover-btn i  { width:14px;text-align:center;color:var(--text2);font-size:0.72rem; }
    .popover-btn.danger  { color:var(--scarlet); }
    .popover-btn.danger i { color:var(--scarlet); }
    .popover-btn.report  { color:#f97316; }
    .popover-btn.report i { color:#f97316; }
    /* Report modal */
    #modal-report-player .modal-box { max-width:370px; }
    #report-target-banner { font-size:0.78rem; color:var(--text2); background:var(--bg2); border:1px solid var(--bg3); border-radius:8px; padding:0.45rem 0.7rem; margin-bottom:0.85rem; }
    #report-target-banner strong { color:var(--text); }
    #report-reason { width:100%; padding:0.42rem 0.6rem; background:var(--bg2); border:1.5px solid var(--bg3); color:var(--text); border-radius:7px; font-size:0.82rem; margin-bottom:0.6rem; }
    #report-details { width:100%; padding:0.42rem 0.6rem; background:var(--bg2); border:1.5px solid var(--bg3); color:var(--text); border-radius:7px; font-size:0.82rem; resize:vertical; min-height:80px; font-family:inherit; }
    #report-details::placeholder { color:var(--text3); }

    /* ── Social Handle Picker ─────────────────────────────────── */
    .handle-input-row { display:flex; gap:0.45rem; align-items:center; }
    .handle-prefix { font-size:1rem; font-weight:700; color:var(--gold); flex-shrink:0; }
    #handle-input { flex:1; min-width:0; font-size:0.92rem; padding:0.4rem 0.6rem; background:var(--bg2); border:1.5px solid var(--bg3); color:var(--text); border-radius:7px; transition:border-color 0.15s; }
    #handle-input.ok   { border-color:#4ade80; }
    #handle-input.bad  { border-color:var(--scarlet); }
    #handle-status { font-size:0.7rem; min-height:1.1em; margin-top:0.2rem; }
    #handle-status.ok   { color:#4ade80; }
    #handle-status.bad  { color:var(--scarlet); }
    #handle-status.idle { color:var(--text2); }
    .share-link-bar { display:flex; gap:0.4rem; align-items:center; margin-top:0.55rem; }
    .share-link-url { flex:1; font-size:0.72rem; font-family:monospace; color:var(--gold); background:var(--bg2); border:1px solid var(--bg3); border-radius:6px; padding:0.3rem 0.5rem; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }

    /* ── Party Social Hub ──────────────────────────────────────── */
    #party-social-wrap { display:flex; gap:0.75rem; height:500px; margin-bottom:0.75rem; }
    #party-friends-col { width:210px; flex-shrink:0; display:flex; flex-direction:column; background:var(--bg1); border:1px solid var(--bg3); border-radius:12px; overflow:hidden; }
    #party-friends-col-header { padding:0.55rem 0.65rem; border-bottom:1px solid var(--bg3); background:var(--bg2); flex-shrink:0; }
    #party-friends-col-header h4 { margin:0; font-size:0.78rem; letter-spacing:0.06em; color:var(--text); display:flex; align-items:center; gap:0.4rem; }
    #party-add-friend-bar { display:flex; gap:0.3rem; margin-top:0.4rem; }
    #party-friend-input { flex:1; font-size:0.72rem; padding:0.28rem 0.5rem; border:1px solid var(--bg3); border-radius:6px; background:var(--bg0); color:var(--text); min-width:0; }
    #party-friend-input:focus { outline:none; border-color:var(--gold); }
    #party-friends-list { flex:1; overflow-y:auto; padding:0.4rem 0.4rem 0.6rem; }
    #party-friends-col .rwhub-contact-row { padding:0.4rem 0.5rem; margin-bottom:0.22rem; border-radius:8px; }
    #party-friends-col .rwhub-contact-avatar { width:32px; height:32px; font-size:0.82rem; }
    #party-friends-col .rwhub-contact-text { gap:0; }
    #party-friends-col .rwhub-contact-name { font-size:0.78rem; }
    #party-friends-col .rwhub-contact-sub  { font-size:0.62rem; }
    #party-friends-col .rwhub-contact-activity { font-size:0.6rem; margin-top:0.06rem; }
    #party-friends-col .rwhub-contact-msg-btn { display:none; } /* no msg btn in compact list */
    #party-friends-col .rwhub-section-label { font-size:0.58rem; padding:0.35rem 0.4rem 0.15rem; }
    /* Chat panel */
    #party-chat-panel { flex:1; min-width:0; display:flex; flex-direction:column; background:var(--bg1); border:1px solid var(--bg3); border-radius:12px; overflow:hidden; }
    #party-chat-empty { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:0.75rem; color:var(--text3); text-align:center; padding:2rem; }
    #party-chat-empty i { font-size:2.2rem; opacity:0.35; }
    #party-chat-empty p { font-size:0.8rem; margin:0; }
    #party-chat-view { display:none; flex:1; flex-direction:column; overflow:hidden; }
    #party-chat-view.active { display:flex; }
    #party-chat-header { display:flex; align-items:center; gap:0.6rem; padding:0.6rem 0.8rem; border-bottom:1px solid var(--bg3); background:var(--bg2); flex-shrink:0; }
    #party-chat-avatar { width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:0.9rem; flex-shrink:0; border:2px solid var(--bg3); }
    #party-chat-info { flex:1; min-width:0; }
    #party-chat-title { font-size:0.88rem; font-weight:700; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    #party-chat-subtitle { font-size:0.63rem; color:var(--text2); margin-top:0.05rem; }
    #party-chat-messages { flex:1; overflow-y:auto; padding:0.75rem; display:flex; flex-direction:column; gap:0.4rem; }
    #party-chat-compose { display:flex; gap:0.45rem; padding:0.5rem 0.65rem; border-top:1px solid var(--bg3); background:var(--bg2); flex-shrink:0; align-items:flex-end; }
    #party-chat-input { flex:1; background:var(--bg0); border:1px solid var(--bg3); border-radius:8px; color:var(--text); font-size:0.82rem; padding:0.42rem 0.65rem; resize:none; min-height:34px; max-height:90px; font-family:inherit; line-height:1.4; overflow-y:auto; }
    #party-chat-input:focus { outline:none; border-color:var(--gold); }
    @media (max-width:599px) {
      /* Friends list takes natural height; no fixed max so the list breathes */
      #party-social-wrap { flex-direction:column; height:auto; gap:0.5rem; }
      #party-friends-col { width:100%; max-height:none; }
      #party-friends-col .rwhub-contact-avatar { width:28px; height:28px; font-size:0.75rem; }

      /* Chat panel slides in over the full screen on mobile */
      #party-chat-panel {
        position:fixed; inset:0; z-index:600;
        border-radius:0; border:none;
        height:100dvh; height:100vh; /* dvh fallback */
        transform:translateX(100%);
        transition:transform 0.22s cubic-bezier(0.4,0,0.2,1);
        display:flex; /* always in DOM, transform controls visibility */
        flex-direction:column;
      }
      #party-chat-panel.mobile-open { transform:translateX(0); }

      /* Back button visible only on mobile */
      #party-chat-back-btn { display:flex !important; }

      /* Bigger touch targets in compose area */
      #party-chat-compose { padding:0.65rem 0.75rem; gap:0.5rem; }
      #party-chat-input { font-size:1rem; min-height:44px; padding:0.6rem 0.75rem; }
      #party-chat-compose .btn { padding:0.55rem 0.9rem; font-size:1rem; }

      /* Message bubbles use more width on mobile */
      .inbox-msg { max-width:92% !important; }
      .inbox-msg.group { max-width:96% !important; }
    }
    /* Back button hidden on desktop */
    #party-chat-back-btn { display:none; }

    /* ── RealmHub shell (mobile-first social hub) ──
       Phase 1 of the inbox redesign. The shell uses a stack model:
       on mobile, only one view is visible at a time and tapping a
       thread row slides the message pane in from the right. On
       desktop ≥720px the root + thread views flow side-by-side. */

    .rwhub-modal {
      /* Widened from 760 → 1100 so the desktop side-by-side layout
         (thread list at left, conversation at right) has real
         breathing room — the previous 760 was so narrow that the
         conversation pane was only ~520 wide, which felt like a
         cramped mobile single-pane view even on a 1920-wide monitor.
         Narrow windows still cap to 100%. */
      max-width: 1100px !important;
      max-height: 92dvh !important;
      width: 100%;
      padding: 0 !important;
      display: flex !important;
      flex-direction: column;
      overflow: hidden;
      position: relative;
    }

    .rwhub-topbar {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      padding: 0.65rem 0.85rem;
      border-bottom: 1px solid var(--bg3);
      background: linear-gradient(135deg, rgba(255,215,0,0.08), transparent);
      flex-shrink: 0;
      min-height: 50px;
    }
    .rwhub-back {
      background: var(--bg2);
      border: 1px solid var(--bg3);
      color: var(--gold);
      width: 34px; height: 34px;
      border-radius: 50%;
      cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      font-size: 0.85rem;
      transition: background 0.15s, transform 0.1s;
      flex-shrink: 0;
    }
    .rwhub-back:active { transform: scale(0.94); background: var(--bg3); }
    .rwhub-title-wrap { flex: 1; min-width: 0; }

    .rwhub-filters {
      display: flex;
      flex-direction: column;
      gap: 0.25rem;
      padding: 0.55rem 0.4rem;
      border-right: 1px solid var(--bg3);
      background: var(--bg1);
      overflow-y: auto;
      flex-shrink: 0;
      width: 108px;
      scrollbar-width: thin;
      -webkit-overflow-scrolling: touch;
    }
    .rwhub-filters::-webkit-scrollbar { width: 4px; }
    /* Mobile: flip inbox to a column layout — horizontal scrolling
       chip strip on top, thread list below. The previous "shrink the
       sidebar to 56px" trick clipped chip labels (most chips have no
       icon to fall back to), and a 56px column ate too much width
       for the thread list to breathe. */
    @media (max-width: 600px) {
      .rwhub-tab-pane[data-pane="inbox"] {
        flex-direction: column !important;
        min-height: 0;
      }
      .rwhub-filters {
        width: 100%;
        flex-direction: row;
        gap: 0.35rem;
        padding: 0.5rem 0.55rem;
        border-right: none;
        border-bottom: 1px solid var(--bg3);
        overflow-x: auto;
        overflow-y: hidden;
        flex-wrap: nowrap;
        flex-shrink: 0;
      }
      .rwhub-chip {
        width: auto;
        white-space: nowrap;
        text-align: center;
        border-radius: 999px;
        font-size: 0.7rem;
        padding: 0.32rem 0.7rem;
      }
    }
    .rwhub-chip {
      flex-shrink: 0;
      font-size: 0.74rem;
      padding: 0.4rem 0.7rem;
      border-radius: 8px;
      background: var(--bg2);
      border: 1px solid var(--bg3);
      color: var(--text2);
      cursor: pointer;
      font-weight: 600;
      letter-spacing: 0.02em;
      text-align: left;
      width: 100%;
      transition: background 0.1s, border-color 0.1s, color 0.1s;
      -webkit-tap-highlight-color: transparent;
    }
    .rwhub-chip.active {
      background: rgba(255,215,0,0.15);
      border-color: var(--gold);
      color: var(--gold);
    }
    .rwhub-chip:active { transform: scale(0.96); }

    .rwhub-stack {
      flex: 1;
      display: flex;
      min-height: 0;
      position: relative;
      overflow: hidden;
    }
    .rwhub-view {
      display: flex;
      flex-direction: column;
      min-height: 0;
      transition: transform 0.25s ease;
      will-change: transform;
    }
    .rwhub-view-root {
      flex: 1;
      width: 100%;
      overflow: hidden;
    }
    .rwhub-view-thread {
      position: absolute;
      inset: 0;
      transform: translateX(100%);
      background: var(--bg1);
      z-index: 5;
    }
    /* When the stack is at "thread", slide the thread view in. */
    [data-stack="thread"] .rwhub-view-thread { transform: translateX(0); }
    /* On desktop, both root and thread are visible side-by-side
       (Slack / Discord / Gmail layout). 280px sidebar gives the
       thread-list rows room for label + snippet + timestamp on one
       line. The back arrow in the topbar is also redundant here
       (the list never leaves the screen) so we hide it. */
    @media (min-width: 720px) {
      .rwhub-view-root   { flex: 0 0 280px; border-right: 1px solid var(--bg3); }
      .rwhub-view-thread { position: static; transform: none !important; flex: 1; z-index: auto; }
      .rwhub-back        { display: none !important; }
    }

    .rwhub-tab-pane { flex: 1; overflow-y: auto; min-height: 0; }

    .rwhub-tabbar {
      display: flex;
      border-top: 1px solid var(--bg3);
      background: var(--bg1);
      flex-shrink: 0;
      padding-bottom: env(safe-area-inset-bottom, 0);
    }
    .rwhub-tab {
      flex: 1;
      background: none;
      border: none;
      padding: 0.55rem 0.3rem 0.5rem;
      cursor: pointer;
      color: var(--text3);
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 0.18rem;
      font-size: 0.62rem;
      letter-spacing: 0.04em;
      font-weight: 600;
      position: relative;
      -webkit-tap-highlight-color: transparent;
      transition: color 0.15s;
    }
    .rwhub-tab i { font-size: 1.05rem; }
    .rwhub-tab.active { color: var(--gold); }
    .rwhub-tab.active::before {
      content: '';
      position: absolute; top: 0; left: 25%; right: 25%;
      height: 2px;
      background: linear-gradient(90deg, var(--gold), var(--scarlet));
      border-radius: 0 0 2px 2px;
    }
    .rwhub-tab:active { transform: scale(0.97); }
    .rwhub-tab-badge {
      position: absolute;
      top: 5px; right: calc(50% - 16px);
      background: var(--scarlet);
      color: #fff;
      border-radius: 999px;
      width: 8px; height: 8px;
      font-size: 0;
    }

    .rwhub-fab {
      position: absolute;
      right: 0.85rem;
      bottom: calc(60px + env(safe-area-inset-bottom, 0) + 0.55rem);
      width: 50px; height: 50px;
      border-radius: 50%;
      background: linear-gradient(135deg, var(--gold), #d4a200);
      color: #1a1a1a;
      border: none;
      box-shadow: 0 4px 14px -2px rgba(0,0,0,0.45), 0 0 0 2px rgba(255,215,0,0.18);
      cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      font-size: 1.05rem;
      z-index: 10;
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.12s, box-shadow 0.15s;
    }
    .rwhub-fab:active { transform: scale(0.92); }
    .rwhub-fab:hover { box-shadow: 0 6px 18px -2px rgba(0,0,0,0.55), 0 0 0 2px rgba(255,215,0,0.32); }
    /* Hide the compose FAB while a thread is open — its position
       collides with the reply bar's Send button on mobile, and the
       reply bar already gives you a way to write. */
    .rwhub-modal[data-stack="thread"] .rwhub-fab { display: none; }
    /* Role-aware visibility on the unified inbox modal.
       _applyInboxRoleVisibility toggles this class on every
       [data-inbox-tab-role] element when the modal opens. */
    .inbox-role-hidden { display: none !important; }
    /* Hide the inbox tabstrip's native scrollbar — wraps long tab
       lists on mobile but the chrome is noisy. */
    .inbox-tabstrip::-webkit-scrollbar { display: none; }

    .rwhub-more-row {
      display: flex;
      align-items: center;
      gap: 0.7rem;
      padding: 0.65rem 0.75rem;
      background: var(--bg2);
      border: 1px solid var(--bg3);
      border-radius: 8px;
      color: var(--text);
      font-size: 0.82rem;
      cursor: pointer;
      width: 100%;
      text-align: left;
      -webkit-tap-highlight-color: transparent;
      transition: background 0.1s;
    }
    .rwhub-more-row:hover { background: var(--bg3); }
    .rwhub-more-row > i:first-child { width: 18px; text-align: center; }
    .rwhub-more-row > span:not(:last-child) { flex: 1; }

    /* ── Calendar (Phase 4) ── */
    .rwhub-cal-header {
      display: flex;
      align-items: center;
      gap: 0.4rem;
      padding: 0.5rem 0.6rem;
      border-bottom: 1px solid var(--bg3);
      background: var(--bg1);
      flex-shrink: 0;
    }
    .rwhub-cal-nav {
      width: 30px; height: 30px;
      border-radius: 50%;
      background: var(--bg2);
      border: 1px solid var(--bg3);
      color: var(--gold);
      cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      font-size: 0.7rem;
      -webkit-tap-highlight-color: transparent;
    }
    .rwhub-cal-nav:active { transform: scale(0.94); background: var(--bg3); }
    .rwhub-cal-title {
      flex: 1;
      background: none;
      border: none;
      color: var(--text);
      font-size: 0.92rem;
      font-weight: 700;
      letter-spacing: 0.04em;
      cursor: pointer;
      text-align: center;
      padding: 0 0.3rem;
      -webkit-tap-highlight-color: transparent;
    }
    .rwhub-cal-toggles {
      display: flex;
      gap: 0.2rem;
      background: var(--bg2);
      border: 1px solid var(--bg3);
      border-radius: 999px;
      padding: 2px;
    }
    .rwhub-cal-toggle {
      background: none;
      border: none;
      color: var(--text2);
      font-size: 0.66rem;
      font-weight: 600;
      letter-spacing: 0.04em;
      padding: 0.2rem 0.55rem;
      border-radius: 999px;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .rwhub-cal-toggle.active {
      background: linear-gradient(135deg, var(--gold), #d4a200);
      color: #1a1a1a;
    }
    .rwhub-cal-body { flex: 1; overflow-y: auto; padding: 0.4rem; }

    .rwhub-cal-grid {
      display: grid;
      grid-template-columns: repeat(7, 1fr);
      gap: 2px;
      background: var(--bg3);
      border-radius: 8px;
      overflow: hidden;
    }
    .rwhub-cal-dow,
    .rwhub-cal-day {
      background: var(--bg1);
      min-height: 0;
    }
    .rwhub-cal-dow {
      text-align: center;
      font-size: 0.6rem;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      font-weight: 700;
      color: var(--text3);
      padding: 0.35rem 0;
    }
    .rwhub-cal-day {
      display: flex;
      flex-direction: column;
      align-items: stretch;
      padding: 0.25rem 0.3rem;
      cursor: pointer;
      min-height: 52px;
      -webkit-tap-highlight-color: transparent;
      transition: background 0.1s;
      position: relative;
    }
    .rwhub-cal-day.muted { opacity: 0.42; }
    .rwhub-cal-day:hover { background: var(--bg2); }
    .rwhub-cal-day.today { background: rgba(230,57,70,0.07); }
    .rwhub-cal-day.today .rwhub-cal-daynum {
      background: var(--scarlet); color: #fff;
      width: 22px; height: 22px;
      border-radius: 50%;
      display: inline-flex; align-items: center; justify-content: center;
      font-weight: 800;
    }
    .rwhub-cal-daynum {
      font-size: 0.72rem;
      font-weight: 700;
      color: var(--text);
      line-height: 1;
      margin-bottom: 0.2rem;
    }
    .rwhub-cal-dots {
      display: flex; flex-wrap: wrap;
      gap: 2px;
      margin-top: auto;
    }
    .rwhub-cal-dot {
      width: 6px; height: 6px; border-radius: 50%;
    }
    .rwhub-cal-dot.session   { background: var(--gold); }
    .rwhub-cal-dot.session-request { background: linear-gradient(135deg, var(--gold), var(--scarlet)); }
    .rwhub-cal-dot.deadline  { background: var(--scarlet); }
    .rwhub-cal-dot.did       { background: var(--blue); }
    .rwhub-cal-dot.lr        { background: #4caf50; }
    .rwhub-cal-dot.recurring { background: var(--purple, #b079e6); }

    .rwhub-cal-agenda { display: flex; flex-direction: column; gap: 0.4rem; padding: 0.2rem; }
    .rwhub-cal-agenda-row {
      display: flex; align-items: center; gap: 0.6rem;
      padding: 0.45rem 0.6rem;
      background: var(--bg2);
      border: 1px solid var(--bg3);
      border-left: 3px solid var(--bg4);
      border-radius: 6px;
      font-size: 0.78rem;
    }
    .rwhub-cal-agenda-row.k-session         { border-left-color: var(--gold); }
    .rwhub-cal-agenda-row.k-session-request { border-left-color: var(--gold); }
    .rwhub-cal-agenda-row.k-deadline        { border-left-color: var(--scarlet); }
    .rwhub-cal-agenda-row.k-did             { border-left-color: var(--blue); }
    .rwhub-cal-agenda-row.k-lr              { border-left-color: #4caf50; }
    .rwhub-cal-agenda-time {
      font-size: 0.66rem; color: var(--text2);
      font-variant-numeric: tabular-nums;
      min-width: 56px;
    }
    .rwhub-cal-agenda-title { flex: 1; font-weight: 600; color: var(--text); }
    .rwhub-cal-agenda-meta  { font-size: 0.66rem; color: var(--text3); }
    .rwhub-cal-agenda-section-label {
      font-size: 0.62rem;
      letter-spacing: 0.12em;
      text-transform: uppercase;
      color: var(--text3);
      font-weight: 700;
      padding: 0.5rem 0.4rem 0.2rem;
    }

    /* ── Quick Actions strip (Inbox tab) ── */
    .rwhub-quickbar {
      display: flex;
      gap: 0.4rem;
      padding: 0.55rem 0.6rem;
      background: linear-gradient(135deg, rgba(255,215,0,0.05), transparent);
      border-bottom: 1px solid var(--bg3);
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: thin;
      flex-shrink: 0;
    }
    .rwhub-quickbar::-webkit-scrollbar { height: 4px; }
    .rwhub-qa {
      flex-shrink: 0;
      display: flex;
      align-items: center;
      gap: 0.35rem;
      padding: 0.4rem 0.7rem;
      background: var(--bg2);
      border: 1px solid var(--bg3);
      border-radius: 999px;
      color: var(--text);
      font-size: 0.74rem;
      font-weight: 600;
      cursor: pointer;
      letter-spacing: 0.02em;
      -webkit-tap-highlight-color: transparent;
      transition: background 0.1s, transform 0.1s, border-color 0.1s;
    }
    .rwhub-qa:active { transform: scale(0.96); }
    .rwhub-qa:hover { background: var(--bg3); }
    .rwhub-qa i { font-size: 0.78rem; opacity: 0.85; }
    .rwhub-qa-primary {
      background: linear-gradient(135deg, var(--gold), #d4a200);
      color: #1a1a1a;
      border-color: var(--gold);
      font-weight: 700;
    }
    .rwhub-qa-primary:hover { background: linear-gradient(135deg, #ffe14d, #d4a200); }
    .rwhub-qa-primary i { opacity: 1; }

    /* ── Contacts list ── */
    /* ── Friends List (game-style) ─────────────────────────────── */
    .rwhub-contact-section { margin-bottom: 0.75rem; }
    .rwhub-section-label {
      font-size: 0.6rem;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: var(--text3);
      font-weight: 800;
      padding: 0.5rem 0.5rem 0.2rem;
      display: flex; align-items: center; gap: 0.4rem;
    }
    .rwhub-section-count {
      background: var(--bg3);
      color: var(--text3);
      border-radius: 999px;
      padding: 0.05rem 0.38rem;
      font-size: 0.58rem;
      font-weight: 700;
    }
    .rwhub-contact-row {
      display: flex;
      align-items: center;
      gap: 0.7rem;
      padding: 0.6rem 0.65rem;
      background: linear-gradient(135deg, var(--bg2), var(--bg1));
      border: 1px solid var(--bg3);
      border-radius: 10px;
      color: var(--text);
      cursor: pointer;
      width: 100%;
      text-align: left;
      margin-bottom: 0.3rem;
      -webkit-tap-highlight-color: transparent;
      transition: background 0.12s, border-color 0.12s, transform 0.1s;
    }
    .rwhub-contact-row.online {
      border-color: rgba(80,200,120,0.28);
      background: linear-gradient(135deg, rgba(80,200,120,0.06), var(--bg2));
    }
    .rwhub-contact-row:hover { background: var(--bg3); border-color: var(--gold); }
    .rwhub-contact-row:active { transform: scale(0.985); }
    /* Avatar: colored circle with class icon + presence dot */
    .rwhub-contact-avatar {
      position: relative;
      width: 42px; height: 42px;
      border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      flex-shrink: 0;
      font-size: 1.05rem;
      border: 2px solid var(--bg3);
    }
    .rwhub-contact-avatar.online { border-color: rgba(80,200,120,0.5); }
    .rwhub-presence-dot {
      position: absolute;
      bottom: -1px; right: -1px;
      width: 11px; height: 11px;
      border-radius: 50%;
      border: 2px solid var(--bg0);
      background: var(--bg3);
    }
    .rwhub-presence-dot.online { background: #50c878; }
    .rwhub-contact-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
    .rwhub-contact-name {
      font-size: 0.87rem;
      font-weight: 700;
      color: var(--text);
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      display: flex; align-items: center; gap: 0.35rem;
    }
    .rwhub-contact-sub {
      font-size: 0.67rem;
      color: var(--text2);
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      margin-top: 0.05rem;
    }
    .rwhub-contact-activity {
      font-size: 0.65rem;
      margin-top: 0.1rem;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .rwhub-contact-activity.active { color: #50c878; }
    .rwhub-contact-activity.idle   { color: var(--text3); }
    /* Inline message button shown on the right */
    .rwhub-contact-msg-btn {
      flex-shrink: 0;
      background: none;
      border: 1px solid var(--bg3);
      border-radius: 7px;
      color: var(--text2);
      padding: 0.28rem 0.55rem;
      font-size: 0.72rem;
      cursor: pointer;
      transition: background 0.1s, color 0.1s, border-color 0.1s;
    }
    .rwhub-contact-msg-btn:hover { background: rgba(255,215,0,0.12); border-color: var(--gold); color: var(--gold); }

    /* On mobile (≤599px) make the modal full-screen so it feels like
       a native app. The existing inbox-modal mobile rule already
       does max-width:100% / max-height:94dvh; we go a bit further. */
    @media (max-width: 599px) {
      #modal-player-inbox.open { padding: 0; align-items: stretch; }
      .rwhub-modal {
        max-width: 100% !important;
        max-height: 100% !important;
        height: 100dvh !important;
        border-radius: 0 !important;
      }
    }

    /* ── Misc ── */

    .text-muted { color: var(--text2); }

    .text-sm    { font-size: 0.8rem; }

    .mt-1 { margin-top: 0.5rem; }

    .mb-1 { margin-bottom: 0.5rem; }

    .empty-state { text-align: center; padding: 2.5rem 1rem; color: var(--text2); font-size: 0.85rem; }

    .empty-state i { font-size: 2rem; opacity: 0.3; display: block; margin-bottom: 0.5rem; }

    /* Sizes below are fluid (clamp()) rather than fixed px/rem, 2026-07-25
       — this is the Hero vitals bar's own identity block (single use,
       confirmed via grep before touching), so scaling it doesn't affect
       any other profile-header/-avatar instance elsewhere in the app.
       Interpolates roughly between a 360px phone and a 1440px desktop;
       .vitals-grid's 640px column-collapse (structural, not a size) is
       unrelated and stays a hard breakpoint. */
    .profile-header {

      display: flex; align-items: center; gap: clamp(0.6rem, 0.4rem + 1vw, 1.2rem); margin-bottom: clamp(0.6rem, 0.5rem + 0.6vw, 1rem);

    }

    .profile-avatar {

      width: clamp(44px, 38px + 2.5vw, 64px); height: clamp(44px, 38px + 2.5vw, 64px); border-radius: 50%;

      background: rgba(255,215,0,0.1); border: 2px solid rgba(255,215,0,0.3);

      display: flex; align-items: center; justify-content: center;

      font-size: clamp(1.15rem, 1rem + 0.6vw, 1.7rem); color: var(--gold); flex-shrink: 0; overflow: hidden;

    }

    .profile-name  { font-size: clamp(1rem, 0.9rem + 0.5vw, 1.4rem); font-weight: 700; }

    .profile-meta  { font-size: clamp(0.7rem, 0.65rem + 0.2vw, 0.88rem); color: var(--text2); margin-top: 0.15rem; }

    .section-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }

    @media (max-width: 640px) { .section-grid { grid-template-columns: 1fr; } }

    .id-badge { font-family: monospace; background: var(--bg2); border: 1px solid var(--bg4); border-radius: 6px; padding: 0.15rem 0.5rem; font-size: 0.75rem; color: var(--text2); }

    /* ── Toast ── */

    #toast-container { position: fixed; bottom: 1.5rem; right: 1.5rem; z-index: 99999; display: flex; flex-direction: column; gap: 0.5rem; transition: bottom 0.15s; }

    /* When the combat modal is open its footer (Roll d20 /
       Advantage / Disadvantage / d20 result) sits at the bottom of
       the centered dialog — which on smaller screens overlaps the
       default toast-container bottom. Lift the toasts above the
       footer so action-pick confirmations don't block the roller.
       openCombatRefModal toggles body.combat-modal-open as a
       JS-side fallback for browsers without :has() support. */
    body:has(#modal-combat-ref.open) #toast-container,
    body.combat-modal-open #toast-container { bottom: 5.5rem; }

    .toast { background: var(--bg2); border: 1px solid var(--bg4); border-radius: 8px; padding: 0.6rem 1rem; font-size: 0.82rem; color: var(--text); box-shadow: 0 4px 20px rgba(0,0,0,0.4); animation: fadeUp 0.25s ease; max-width: 340px; position: relative; overflow: hidden; cursor: pointer; }
    .toast .toast-progress { position: absolute; bottom: 0; left: 0; height: 2px; background: var(--gold); animation: toastTimer linear forwards; }
    .toast.success { border-color: rgba(76,175,80,0.5); }
    .toast.success .toast-progress { background: rgba(76,175,80,0.7); }
    .toast.warn    { border-color: rgba(255,193,7,0.5); }
    .toast.warn .toast-progress { background: rgba(255,193,7,0.7); }
    .toast.error   { border-color: rgba(224,85,85,0.5); }
    .toast.error .toast-progress { background: rgba(224,85,85,0.7); }
    .toast.fadeOut  { animation: fadeOut 0.3s ease forwards; }
    @keyframes fadeUp { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
    @keyframes fadeOut { from { opacity:1; transform:translateY(0); } to { opacity:0; transform:translateY(-8px); } }
    @keyframes toastTimer { from { width: 100%; } to { width: 0%; } }

    /* Skeleton loading placeholders */
    .skeleton { background: var(--bg3); border-radius: 6px; position: relative; overflow: hidden; }
    .skeleton::after { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.04), transparent); animation: skeletonShimmer 1.5s infinite; }
    @keyframes skeletonShimmer { to { left: 100%; } }
    .skeleton-card { height: 72px; margin-bottom: 0.5rem; }
    .skeleton-line { height: 14px; margin-bottom: 0.4rem; }
    .skeleton-line.short { width: 40%; }
    .skeleton-line.medium { width: 65%; }
    .skeleton-line.long { width: 90%; }

    .skills-list { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.4rem; }

    .skill-tag { background: rgba(255,215,0,0.08); border: 1px solid rgba(255,215,0,0.2); border-radius: 20px; padding: 0.15rem 0.55rem; font-size: 0.72rem; color: var(--gold); cursor: help; }

    .skill-tag.dim { background: rgba(255,255,255,0.03); border: 1px solid var(--bg3); color: var(--text2); }

    .skill-tag.prof { background: rgba(255,215,0,0.15); border: 1px solid var(--gold); color: var(--gold); font-weight: 600; }

    .skill-tag.exp { background: linear-gradient(135deg, rgba(255,215,0,0.22), rgba(255,140,0,0.18)); border: 1px solid #ffb84d; color: #ffe066; font-weight: 700; box-shadow: 0 0 8px rgba(255,180,60,0.25); }

    .skill-tag .skill-mod { opacity: 0.75; margin-left: 0.3rem; font-variant-numeric: tabular-nums; }

    .skill-tag.prof .skill-mod, .skill-tag.exp .skill-mod { opacity: 1; }

    /* Passive perception / investigation tiles. Color coded by their
       driving ability per the same palette as ability scores +
       skills: WIS=purple (Perception), INT=blue (Investigation). */
    .passive-tile { flex: 1; min-width: 120px; background: var(--bg2); border: 1px solid var(--bg3); border-left: 3px solid var(--bg4); border-radius: 8px; padding: 0.45rem 0.6rem; cursor: help; transition: border-color 0.15s; }
    .passive-tile .passive-label { font-size: 0.6rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.08em; }
    .passive-tile .passive-val { font-size: 1.15rem; font-weight: 700; color: var(--gold); margin-top: 0.15rem; font-variant-numeric: tabular-nums; }
    .passive-tile[data-ab="wis"] { border-left-color: var(--purple); }
    .passive-tile[data-ab="wis"] .passive-label,
    .passive-tile[data-ab="wis"] .passive-val { color: var(--purple); }
    .passive-tile[data-ab="int"] { border-left-color: var(--blue); }
    .passive-tile[data-ab="int"] .passive-label,
    .passive-tile[data-ab="int"] .passive-val { color: var(--blue); }
    .passive-tile[data-ab="str"] { border-left-color: var(--scarlet); }
    .passive-tile[data-ab="str"] .passive-label,
    .passive-tile[data-ab="str"] .passive-val { color: var(--scarlet); }

    /* Saving throw tiles */
    /* Save tiles — same heraldic palette as ability scores + skills.
       Each tile is tagged with data-ab="str|dex|…" so its left
       border, label, and value all read in the matching tone (STR=
       scarlet, DEX=green, CON=orange, INT=blue, WIS=purple, CHA=
       gold). The .prof state still wins the visual hierarchy: a
       brighter tinted background + the PROF badge so proficient
       saves are unambiguously "the strong ones." */
    /* Sized to match #p-combat-stats .stat-box (2026-07-22) — save-tile
       now lives on the vitals-bar right next to those combat stats, so
       it should read as a peer, not a smaller chip. Only render site is
       #p-saves-list (renderSaves() in rw-core.js), so safe to resize
       without affecting anything else. */
    .save-tile { display: flex; flex-direction: column; align-items: center; padding: 0.45rem 0.6rem; background: var(--bg2); border: 1px solid var(--bg3); border-left: 3px solid var(--bg4); border-radius: 6px; min-width: 60px; cursor: help; }
    .save-tile.prof { background: rgba(255,215,0,0.1); }
    .save-tile .save-label { font-size: 0.64rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.08em; }
    .save-tile .save-val { font-size: 1.3rem; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
    .save-tile .save-flag { font-size: 0.58rem; color: var(--gold); opacity: 0; }
    .save-tile.prof .save-flag { opacity: 1; }
    /* Per-ability accent — applies whether or not the save is
       proficient, so even a non-prof save is visually grouped with
       its driving stat. Proficiency adds a tinted background +
       PROF badge on top of the per-ability color. */
    .save-tile[data-ab="str"] { border-left-color: var(--scarlet); }
    .save-tile[data-ab="dex"] { border-left-color: #4caf50; }
    .save-tile[data-ab="con"] { border-left-color: #e89c44; }
    .save-tile[data-ab="int"] { border-left-color: var(--blue); }
    .save-tile[data-ab="wis"] { border-left-color: var(--purple); }
    .save-tile[data-ab="cha"] { border-left-color: var(--gold); }
    .save-tile[data-ab="str"] .save-label,
    .save-tile[data-ab="str"] .save-val { color: var(--scarlet); }
    .save-tile[data-ab="dex"] .save-label,
    .save-tile[data-ab="dex"] .save-val { color: #4caf50; }
    .save-tile[data-ab="con"] .save-label,
    .save-tile[data-ab="con"] .save-val { color: #e89c44; }
    .save-tile[data-ab="int"] .save-label,
    .save-tile[data-ab="int"] .save-val { color: var(--blue); }
    .save-tile[data-ab="wis"] .save-label,
    .save-tile[data-ab="wis"] .save-val { color: var(--purple); }
    .save-tile[data-ab="cha"] .save-label,
    .save-tile[data-ab="cha"] .save-val { color: var(--gold); }

    /* Skills grouped by governing ability */
    .skills-group { display: flex; align-items: flex-start; gap: 0.5rem; padding: 0.3rem 0 0.3rem 0.55rem; border-bottom: 1px dashed var(--bg3); border-left: 3px solid transparent; transition: border-color 0.15s; }
    .skills-group:last-child { border-bottom: none; }
    .skills-group-label { flex: 0 0 2.4rem; font-size: 0.68rem; font-weight: 700; color: var(--gold); text-transform: uppercase; letter-spacing: 0.08em; padding-top: 0.25rem; }

    /* Per-ability accent stripe + label color, matching the ability
       score boxes above. STR / DEX / CON / INT / WIS / CHA each get
       a distinct heraldic tone so the player can scan the skill list
       by ability without reading every label. */
    .skills-group[data-ab="str"] { border-left-color: var(--scarlet); }
    .skills-group[data-ab="dex"] { border-left-color: #4caf50; }
    .skills-group[data-ab="con"] { border-left-color: #e89c44; }
    .skills-group[data-ab="int"] { border-left-color: var(--blue); }
    .skills-group[data-ab="wis"] { border-left-color: var(--purple); }
    .skills-group[data-ab="cha"] { border-left-color: var(--gold); }
    .skills-group[data-ab="str"] .skills-group-label { color: var(--scarlet); }
    .skills-group[data-ab="dex"] .skills-group-label { color: #4caf50; }
    .skills-group[data-ab="con"] .skills-group-label { color: #e89c44; }
    .skills-group[data-ab="int"] .skills-group-label { color: var(--blue); }
    .skills-group[data-ab="wis"] .skills-group-label { color: var(--purple); }
    .skills-group[data-ab="cha"] .skills-group-label { color: var(--gold); }
    .skills-group-tags { display: flex; flex-wrap: wrap; gap: 0.3rem; flex: 1; }

    .no-data-note { font-size: 0.75rem; color: var(--text2); font-style: italic; margin-top: 0.5rem; }

    .blurred { filter: blur(5px); cursor: pointer; user-select: none; transition: filter 0.2s; }

    .blurred:hover { filter: blur(3px); }

    .blurred.revealed { filter: none; cursor: default; }

    /* ── Push notification badge ── */

    .notif-badge { display:inline-flex;align-items:center;justify-content:center;background:var(--red);color:#fff;border-radius:50%;width:18px;height:18px;font-size:0.65rem;font-weight:700;margin-left:0.3rem;vertical-align:middle; }

    .incoming-row { display:flex;align-items:center;gap:0.75rem;padding:0.65rem 0;border-bottom:1px solid var(--bg2);flex-wrap:wrap; }

    .incoming-row:last-child { border-bottom:none; }

    .incoming-row .i-name { font-weight:600;font-size:0.88rem;flex:1; }

    .incoming-row .i-meta { font-size:0.75rem;color:var(--text2); }

    .incoming-row.unseen { background:rgba(255,215,0,0.04);border-radius:6px;padding:0.65rem 0.5rem; }

    /* ── Admin styles ── */

    #screen-admin { flex-direction: column; background: var(--bg0); }

    .admin-tabs { display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center; margin-bottom: 1.25rem; border-bottom: 1px solid var(--bg3); padding-bottom: 0.75rem; }

    .admin-tab-sep { display: inline-block; width: 1px; height: 1.4rem; background: var(--bg3); margin: 0 0.15rem; align-self: center; }

    .admin-tab-group-label { display: inline-block; font-size: 0.6rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.08em; margin-right: 0.2rem; opacity: 0.7; }

    /* ── Mobile admin nav (drawer + sticky header) ─────────────── */

    .admin-mobile-header { display: none; }

    .admin-drawer-backdrop { display: none; }

    .admin-drawer { display: none; }

    @media (max-width: 900px) {

      .admin-tabs { display: none !important; }

      .admin-mobile-header {

        display: flex; align-items: center; gap: 0.6rem;

        background: var(--bg1); border: 1px solid var(--bg3); border-radius: 10px;

        padding: 0.55rem 0.75rem; margin-bottom: 0.75rem;

        position: sticky; top: 0.5rem; z-index: 40;

        box-shadow: 0 2px 8px rgba(0,0,0,0.25);

      }

      .admin-mobile-header .hamburger {

        background: transparent; border: 0; color: var(--gold); font-size: 1.15rem;

        padding: 0.25rem 0.45rem; cursor: pointer;

      }

      .admin-mobile-header .current-tab-label {

        font-size: 0.92rem; font-weight: 700; color: var(--text1);

        display: flex; align-items: center; gap: 0.45rem;

        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;

      }

      .admin-drawer-backdrop {

        display: block; position: fixed; inset: 0;

        background: rgba(0,0,0,0.55); z-index: 998;

        opacity: 0; pointer-events: none; transition: opacity 0.2s;

      }

      .admin-drawer-backdrop.open { opacity: 1; pointer-events: auto; }

      .admin-drawer {

        display: flex; flex-direction: column;

        position: fixed; top: 0; left: 0; bottom: 0; width: 82%; max-width: 320px;

        background: var(--bg1); border-right: 1px solid var(--bg3);

        z-index: 999; transform: translateX(-105%);

        transition: transform 0.22s ease-out;

        box-shadow: 6px 0 24px rgba(0,0,0,0.45);

        overflow-y: auto;

      }

      .admin-drawer.open { transform: translateX(0); }

      .admin-drawer-header {

        display: flex; align-items: center; justify-content: space-between;

        padding: 0.9rem 1rem 0.65rem; border-bottom: 1px solid var(--bg3);

      }

      .admin-drawer-header strong { font-size: 0.95rem; color: var(--gold); }

      .admin-drawer-header .close {

        background: transparent; border: 0; color: var(--text2);

        font-size: 1.1rem; padding: 0.2rem 0.45rem; cursor: pointer;

      }

      .admin-drawer-group { padding: 0.65rem 0.6rem 0.3rem; }

      .admin-drawer-group-label {

        font-size: 0.62rem; color: var(--text2); text-transform: uppercase;

        letter-spacing: 0.09em; padding: 0 0.5rem; margin-bottom: 0.3rem;

      }

      .admin-drawer-btn {

        display: flex; align-items: center; gap: 0.55rem;

        width: 100%; padding: 0.7rem 0.85rem; margin-bottom: 0.25rem;

        background: transparent; border: 1px solid transparent; border-radius: 8px;

        color: var(--text1); font-size: 0.88rem; text-align: left; cursor: pointer;

        transition: background 0.12s, border-color 0.12s;

      }

      .admin-drawer-btn:active, .admin-drawer-btn:hover {

        background: rgba(255,215,0,0.08); border-color: rgba(255,215,0,0.35);

      }

      .admin-drawer-btn.active {

        background: rgba(255,215,0,0.14); border-color: var(--gold); color: var(--gold); font-weight: 700;

      }

      .admin-drawer-btn i { width: 18px; text-align: center; color: var(--text2); }

      .admin-drawer-btn.active i { color: var(--gold); }

      /* Tighten card and form padding on narrow screens */

      #screen-admin .card { padding: 0.75rem 0.85rem; }

      #screen-admin .form-group { margin-bottom: 0.55rem; }

      #screen-admin h2 { font-size: 1rem; }

      #screen-admin h3 { font-size: 0.9rem; }

      #screen-admin .page-title { font-size: 1rem; margin-bottom: 0.6rem; }

      .dashboard-body { padding: 0 0.5rem 0.75rem !important; }
      .dash-panel:not(:has(> .hero-subnav)) { padding-top: 0.75rem !important; }

    }

    .admin-tab { background: var(--bg2); border: 1px solid var(--bg3); color: var(--text2); border-radius: 8px; padding: 0.4rem 0.9rem; font-size: 0.8rem; font-weight: 600; cursor: pointer; transition: all 0.15s; }

    .admin-tab:hover { border-color: var(--gold); color: var(--gold); }

    .admin-tab.active { background: rgba(255,215,0,0.1); border-color: var(--gold); color: var(--gold); }

    .admin-section { display: none; }

    .admin-section.active { display: block; }

    /* ── Admin inbox ── */
    .inbox-chip {
      background: var(--bg2); border: 1px solid var(--bg3); color: var(--text2);
      border-radius: 999px; padding: 0.35rem 0.75rem; font-size: 0.78rem;
      font-weight: 600; cursor: pointer; transition: all 0.15s;
      display: inline-flex; align-items: center; gap: 0.35rem;
      min-height: 36px;
    }
    .inbox-chip:hover { border-color: var(--gold); color: var(--gold); }
    .inbox-chip.active { background: var(--gold); border-color: var(--gold); color: #3a2400; }
    .inbox-chip-count {
      background: var(--bg3); color: var(--text1); font-size: 0.68rem;
      border-radius: 999px; padding: 0.05rem 0.45rem; font-weight: 700;
      min-width: 1.3em; text-align: center;
    }
    .inbox-chip.active .inbox-chip-count { background: rgba(0,0,0,0.2); color: #3a2400; }
    .inbox-chip-count:empty { display: none; }

    .inbox-list { display: flex; flex-direction: column; gap: 0.3rem; }

    .inbox-row {
      display: grid;
      grid-template-columns: auto 1fr auto;
      gap: 0.55rem;
      padding: 0.65rem 0.75rem;
      background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px;
      cursor: pointer; transition: all 0.15s; align-items: start;
    }
    .inbox-row:hover { border-color: var(--gold); background: var(--bg3); }
    .inbox-row.unread { border-left: 3px solid var(--gold); }
    .inbox-row.unread .inbox-row-subject { font-weight: 700; }

    .inbox-row-icon {
      width: 38px; height: 38px; border-radius: 8px;
      display: flex; align-items: center; justify-content: center;
      font-size: 1rem; flex-shrink: 0;
    }
    .inbox-row-icon.type-password { background: rgba(107,160,255,0.15); color: #6ba0ff; }
    .inbox-row-icon.type-session  { background: rgba(76,175,80,0.15);   color: #4caf50; }
    .inbox-row-icon.type-card     { background: rgba(212,175,55,0.15);  color: var(--gold); }
    .inbox-row-icon.type-bug      { background: rgba(224,85,85,0.15);   color: var(--red); }

    .inbox-row-main { min-width: 0; }
    .inbox-row-header {
      display: flex; justify-content: space-between; gap: 0.5rem;
      font-size: 0.8rem; margin-bottom: 2px;
    }
    .inbox-row-from { font-weight: 600; color: var(--text1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .inbox-row-time { color: var(--text2); font-size: 0.7rem; flex-shrink: 0; white-space: nowrap; }
    .inbox-row-subject { font-size: 0.82rem; color: var(--text1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .inbox-row-snippet { font-size: 0.72rem; color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px; }

    .inbox-row-actions {
      display: flex; flex-direction: column; gap: 0.25rem; align-items: flex-end;
      flex-shrink: 0;
    }
    .inbox-row-status {
      font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.05em;
      padding: 2px 8px; border-radius: 999px; font-weight: 700; white-space: nowrap;
    }

    @media (max-width: 640px) {
      .inbox-row { grid-template-columns: auto 1fr; }
      .inbox-row-actions { grid-column: 1 / -1; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: flex-end; }
    }

    /* ── Shared mail (company_emails) styles. Used by both the
       player HR-tab inbox card and the DM Mail tab. The same
       row markup ships in both places so the visual hierarchy +
       click affordance stay identical. ─────────────────────── */
    .mail-filters {
      display: flex; gap: 0.4rem; padding: 0.5rem 0.1rem 0.7rem;
      flex-wrap: wrap; overflow-x: auto;
    }
    .mail-filters::-webkit-scrollbar { display: none; }
    .mail-filter-chip {
      display: inline-flex; align-items: center; gap: 0.3rem;
      padding: 0.35rem 0.7rem; font-size: 0.74rem;
      background: var(--bg2); border: 1px solid var(--bg3); color: var(--text);
      border-radius: 999px; cursor: pointer; white-space: nowrap;
      transition: all 0.15s; font-weight: 500;
    }
    .mail-filter-chip:hover { border-color: var(--gold); }
    .mail-filter-chip.active {
      background: var(--gold); color: #3a2400; border-color: var(--gold); font-weight: 700;
    }
    .mail-filter-chip-count {
      font-size: 0.62rem; background: var(--bg3); color: var(--text2);
      border-radius: 999px; padding: 0.05rem 0.4rem; font-weight: 700; min-width: 1.2em; text-align: center;
    }
    .mail-filter-chip.active .mail-filter-chip-count { background: rgba(0,0,0,0.18); color: #3a2400; }
    .mail-filter-chip-count:empty { display: none; }
    .mail-list { display: flex; flex-direction: column; gap: 0.4rem; }
    .mail-card {
      display: flex; align-items: stretch; gap: 0.6rem;
      padding: 0.65rem 0.8rem;
      background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px;
      cursor: pointer; transition: all 0.15s;
      width: 100%; text-align: left; color: var(--text); font: inherit;
    }
    .mail-card:hover  { border-color: var(--gold); background: var(--bg3); }
    .mail-card:focus  { outline: 2px solid var(--gold); outline-offset: 1px; }
    .mail-card.unread { border-left: 3px solid var(--gold); }
    .mail-card.unread .mail-card-subject { font-weight: 700; }
    .mail-card.read   { opacity: 0.85; }
    .mail-card-icon {
      width: 32px; height: 32px; border-radius: 7px;
      display: flex; align-items: center; justify-content: center;
      font-size: 0.85rem; flex-shrink: 0;
      background: rgba(218,165,32,0.12); color: var(--gold);
    }
    .mail-card-icon.form  { background: rgba(218,165,32,0.18); color: var(--gold); }
    .mail-card-icon.sent  { background: rgba(106,164,224,0.15); color: var(--blue); }
    .mail-card-icon.plain { background: rgba(255,255,255,0.06); color: var(--text2); }
    .mail-card-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
    .mail-card-row {
      display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem;
    }
    .mail-card-from {
      font-size: 0.82rem; color: var(--text);
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0;
    }
    .mail-card-time {
      font-size: 0.66rem; color: var(--text3); flex-shrink: 0; white-space: nowrap;
    }
    .mail-card-subject {
      font-size: 0.78rem; color: var(--text2);
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .mail-card-tags { display: flex; gap: 0.3rem; margin-top: 0.2rem; flex-wrap: wrap; }
    .mail-tag {
      font-size: 0.6rem; padding: 0.05rem 0.4rem; border-radius: 4px;
      letter-spacing: 0.04em; text-transform: uppercase; font-weight: 700;
    }
    .mail-tag.form      { background: rgba(218,165,32,0.18); color: var(--gold); }
    .mail-tag.broadcast { background: rgba(106,164,224,0.15); color: var(--blue); }
    .mail-tag.sent      { background: rgba(255,255,255,0.06); color: var(--text2); }
    .mail-tag.unread    { background: var(--gold); color: #3a2400; }
    .mail-empty {
      text-align: center; color: var(--text2); font-size: 0.82rem;
      padding: 1.5rem 0.75rem; background: var(--bg2);
      border: 1px dashed var(--bg3); border-radius: 8px;
    }

    .data-store-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.6rem 0; border-bottom: 1px solid var(--bg2); flex-wrap: wrap; }

    .data-store-row:last-child { border-bottom: none; }

    .ds-key { font-family: monospace; font-size: 0.8rem; color: var(--gold); min-width: 160px; }

    .ds-count { font-size: 0.75rem; color: var(--text2); min-width: 80px; }

    .ds-actions { display: flex; gap: 0.4rem; margin-left: auto; }

    .upload-zone { border: 2px dashed var(--bg4); border-radius: 10px; padding: 2rem; text-align: center; color: var(--text2); font-size: 0.85rem; cursor: pointer; transition: border-color 0.15s; }

    .upload-zone:hover { border-color: var(--gold); color: var(--gold); }

    .upload-zone i { font-size: 2rem; display: block; margin-bottom: 0.5rem; opacity: 0.5; }

    .json-preview { background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px; padding: 0.75rem; font-family: monospace; font-size: 0.72rem; color: var(--text2); max-height: 260px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; margin-top: 0.75rem; }

    .agreement-row { display: grid; grid-template-columns: 1fr 1fr auto; gap: 0.75rem; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid var(--bg2); font-size: 0.82rem; }

    .agreement-row:last-child { border-bottom: none; }

    /* ── Character Creator Wizard ── */

    #screen-wizard { flex-direction: column; background: var(--bg0); }

    .wiz-progress-bar { height: 3px; background: var(--bg3); }

    .wiz-progress-fill { height: 100%; background: var(--gold); transition: width 0.3s; }

    .wiz-step-label { font-size: 0.72rem; color: var(--text2); text-align: center; padding: 0.4rem 0 0; text-transform: uppercase; letter-spacing: 0.1em; }

    .wiz-step { display: none; }

    .wiz-step.active { display: block; }

    .wiz-step-title { font-size: 1.1rem; font-weight: 700; color: var(--gold); margin-bottom: 0.25rem; }

    .wiz-step-sub { font-size: 0.8rem; color: var(--text2); margin-bottom: 1.25rem; }

    /* ── World Map modal layout ────────────────────────────────────
       Default: two-column (map left, 240px side panel right). On
       narrow viewports (< 720px) collapses to a vertical stack and
       reveals the "Show / Hide List" toggle so the map can claim the
       full width while the side panel slides up from the bottom on
       demand. Stays mobile-touch-friendly by tightening padding +
       hiding non-critical button labels. */
    .wm-body { display: flex; gap: 0.6rem; flex: 1; min-height: 0; overflow: hidden; }
    .wm-map-wrap { flex: 1; min-width: 0; background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px; overflow: hidden; position: relative; }
    .wm-side-panel { width: 240px; flex-shrink: 0; display: flex; flex-direction: column; gap: 0.4rem; overflow-y: auto; max-height: 100%; }
    .wm-list-toggle { position: absolute; top: 0.5rem; right: 0.5rem; z-index: 1000; background: rgba(26, 26, 46, 0.92); border: 1px solid rgba(180, 140, 255, 0.5); color: #b48cff; font-size: 0.72rem; padding: 0.3rem 0.55rem; box-shadow: 0 2px 6px rgba(0,0,0,0.3); }
    .wm-list-toggle:hover { background: rgba(180, 140, 255, 0.18); }
    @media (max-width: 720px) {
      .wm-body { flex-direction: column; gap: 0.4rem; }
      .wm-map-wrap { flex: 1; min-height: 50dvh; }
      .wm-side-panel { width: 100%; max-height: 40dvh; border-top: 1px solid var(--bg3); padding-top: 0.4rem; }
      .wm-list-toggle { display: inline-flex !important; align-items: center; gap: 0.3rem; }
      /* When the side panel is collapsed on mobile, hide it and give
         the map the full modal height. JS toggles .wm-list-collapsed
         on the .wm-body wrapper. */
      .wm-body.wm-list-collapsed .wm-side-panel { display: none; }
      .wm-body.wm-list-collapsed .wm-map-wrap { min-height: 70dvh; }
      /* Tighten toolbar buttons; the dropdown carries the verbose
         labels, but the mode buttons can drop their text. */
      #modal-world-map .modal { padding: 0.5rem 0.6rem; }
    }
    .wiz-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-bottom: 1rem; }

    .wiz-grid-2 { grid-template-columns: repeat(2, 1fr); }

    .wiz-grid-4 { grid-template-columns: repeat(4, 1fr); }

    @media (max-width: 480px) { .wiz-grid { grid-template-columns: repeat(2, 1fr); } .wiz-grid-4 { grid-template-columns: repeat(3, 1fr); } }

    .wiz-item-btn {

      background: var(--bg2); border: 2px solid var(--bg3); border-radius: 10px;

      padding: 0.6rem 0.4rem; cursor: pointer; text-align: center; transition: all 0.15s;

      font-family: inherit; color: var(--text);

    }

    .wiz-item-btn:hover { border-color: var(--gold-dim); }

    .wiz-item-btn.wiz-selected { border-color: var(--gold); background: rgba(255,215,0,0.1); }

    /* Inline-expansion details — appear ONLY on the selected card.
       Selected card spans the full row so the detail has room without
       breaking the grid into uneven heights. */
    .wiz-item-btn.wiz-selected { grid-column: 1 / -1; padding: 0.9rem 1rem; }
    .wiz-item-btn .wi-detail { display: none; }
    .wiz-item-btn.wiz-selected .wi-detail {
      display: block; margin-top: 0.55rem; padding-top: 0.55rem;
      border-top: 1px solid rgba(255,215,0,0.25);
      text-align: left; font-size: 0.74rem; line-height: 1.5; color: var(--text);
    }
    .wi-detail .wi-row { margin: 0.28rem 0; }
    .wi-detail .wi-row b { color: var(--gold); font-weight: 700; }
    .wi-detail .wi-traits { display: flex; flex-direction: column; gap: 0.2rem; margin: 0.2rem 0; }
    .wi-detail .wi-trait { font-size: 0.71rem; padding: 0.2rem 0; border-top: 1px dashed var(--bg3); }
    .wi-detail .wi-trait:first-child { border-top: none; }
    .wi-detail .wi-trait b { color: var(--gold-dim); }

    .wiz-item-btn .wi-emoji { font-size: 1.4rem; display: block; margin-bottom: 0.2rem; }

    .wiz-item-btn .wi-name { font-size: 0.78rem; font-weight: 700; display: block; }

    .wiz-item-btn .wi-sub  { font-size: 0.62rem; color: var(--text2); display: block; margin-top: 0.1rem; }

    .wiz-item-btn .wi-debt { font-size: 0.6rem; color: var(--bg0); display: block; margin-top: 0.1rem; background: var(--bg0); } /* hidden — matches bg */

    .wiz-ab-method-tabs { display: flex; gap: 0.35rem; margin-bottom: 1rem; flex-wrap: wrap; }

    .wiz-ab-tab { padding: 0.4rem 0.85rem; border: 1px solid var(--bg3); border-radius: 20px; background: var(--bg2); color: var(--text2); font-size: 0.78rem; font-weight: 600; cursor: pointer; transition: all 0.15s; font-family: inherit; }

    .wiz-ab-tab:hover { border-color: var(--gold-dim); color: var(--text); }

    .wiz-ab-tab.active { border-color: var(--gold); background: rgba(255,215,0,0.1); color: var(--gold); }

    .wiz-ab-panel { display: none; }

    .wiz-ab-panel.active { display: block; }

    .wiz-score-pool { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.75rem; min-height: 44px; }

    .wiz-pool-chip {

      padding: 0.4rem 0.75rem; border: 2px solid var(--bg4); border-radius: 8px;

      background: var(--bg2); font-weight: 700; font-size: 1rem; cursor: pointer;

      transition: all 0.15s; font-family: inherit; color: var(--text);

    }

    .wiz-pool-chip:hover { border-color: var(--gold-dim); }

    .wiz-pool-chip.selected { border-color: var(--gold); background: rgba(255,215,0,0.15); color: var(--gold); }

    .wiz-pool-chip.used { display: none; }

    .wiz-stats-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 0.4rem; margin-bottom: 0.75rem; }

    @media (max-width: 480px) { .wiz-stats-grid { grid-template-columns: repeat(3, 1fr); } }

    .wiz-stat-slot {

      background: var(--bg2); border: 2px solid var(--bg3); border-radius: 8px;

      padding: 0.5rem 0.25rem; text-align: center; cursor: pointer; transition: all 0.15s;

    }

    .wiz-stat-slot:hover { border-color: var(--gold-dim); }

    .wiz-stat-slot.ready { border-color: var(--gold); }

    .wiz-stat-slot.assigned { border-color: var(--green); }

    .wiz-stat-slot .ss-label { font-size: 0.58rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.1em; }

    .wiz-stat-slot .ss-val   { font-size: 1.15rem; font-weight: 700; color: var(--gold); line-height: 1.1; }

    .wiz-stat-slot .ss-final { font-size: 0.7rem; color: var(--green); font-weight: 600; }

    .wiz-debt-card { background: rgba(224,85,85,0.08); border: 1px solid rgba(224,85,85,0.3); border-radius: 10px; padding: 0.75rem 1rem; margin-top: 0.75rem; display: none; }

    .wiz-debt-card .dc-amount { font-size: 1.4rem; font-weight: 800; color: var(--red); }

    .wiz-debt-card .dc-note { font-size: 0.75rem; color: var(--text2); margin-top: 0.15rem; }

    .wiz-bg-select { width: 100%; padding: 0.6rem 0.8rem; background: var(--bg2); border: 1px solid var(--bg4); border-radius: 8px; color: var(--text); font-size: 0.92rem; font-family: inherit; margin-bottom: 0.75rem; }

    .wiz-bg-feature { background: rgba(255,215,0,0.06); border: 1px solid rgba(255,215,0,0.15); border-radius: 8px; padding: 0.65rem 0.8rem; font-size: 0.8rem; color: var(--text2); margin-bottom: 0.75rem; min-height: 2.5rem; }

    .wiz-skill-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.3rem; }

    .wiz-skill-item { display: flex; align-items: center; gap: 0.45rem; padding: 0.3rem 0.45rem; border-radius: 6px; cursor: pointer; font-size: 0.8rem; transition: background 0.1s; }

    .wiz-skill-item:hover { background: var(--bg2); }

    .wiz-skill-item input { accent-color: var(--gold); }

    .wiz-skill-item.bg-skill { color: var(--gold); }

    .wiz-skill-item.locked { opacity: 0.5; pointer-events: none; }

    .wiz-review-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-bottom: 1rem; }

    @media (max-width: 500px) { .wiz-review-grid { grid-template-columns: 1fr; } }

    .wiz-review-block { background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px; padding: 0.75rem; }

    .wiz-review-block h4 { font-size: 0.7rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 0.5rem; }

    .wiz-scores-display { display: grid; grid-template-columns: repeat(6,1fr); gap: 0.3rem; margin-top: 0.3rem; }

    .wiz-score-cell { text-align: center; background: var(--bg3); border-radius: 6px; padding: 0.3rem; }

    .wiz-score-cell .sc-label { font-size: 0.55rem; color: var(--text2); text-transform: uppercase; }

    .wiz-score-cell .sc-val   { font-size: 0.95rem; font-weight: 700; color: var(--gold); }

    .wiz-score-cell .sc-mod   { font-size: 0.62rem; color: var(--text2); }

    .wiz-nav { display: flex; gap: 0.75rem; justify-content: flex-end; padding: 1.5rem 0 0.5rem; }

    .wiz-reroll-info { font-size: 0.72rem; color: var(--text2); margin-bottom: 0.5rem; }

    .wiz-reroll-cost { color: var(--bg0); font-size: 0.7rem; background: var(--bg0); } /* hidden */

    .wiz-pb-grid { display: grid; grid-template-columns: repeat(6,1fr); gap: 0.4rem; margin-bottom: 0.5rem; }

    @media (max-width: 480px) { .wiz-pb-grid { grid-template-columns: repeat(3,1fr); } }

    .wiz-pb-cell { background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px; padding: 0.5rem 0.25rem; text-align: center; }

    .wiz-pb-cell .pb-label { font-size: 0.58rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.25rem; }

    .wiz-pb-adj { display: flex; align-items: center; justify-content: center; gap: 0.3rem; }

    .wiz-pb-adj button { width: 22px; height: 22px; border: 1px solid var(--bg4); border-radius: 4px; background: var(--bg3); color: var(--text); font-size: 0.9rem; font-weight: 700; cursor: pointer; line-height: 1; font-family: inherit; }

    .wiz-pb-adj button:hover { border-color: var(--gold); }

    .wiz-pb-val { font-size: 1.05rem; font-weight: 700; min-width: 22px; text-align: center; }

    .wiz-pb-final { font-size: 0.68rem; color: var(--green); font-weight: 600; margin-top: 0.15rem; min-height: 1em; }

    .wiz-pb-points { font-size: 0.82rem; color: var(--text2); margin-bottom: 0.5rem; }

    .wiz-sa-grid { display: grid; grid-template-columns: repeat(6,1fr); gap: 0.4rem; margin-bottom: 0.5rem; }

    @media (max-width: 480px) { .wiz-sa-grid { grid-template-columns: repeat(3,1fr); } }

    .wiz-sa-cell { background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px; padding: 0.5rem 0.25rem; text-align: center; }

    .wiz-sa-cell .sa-label { font-size: 0.58rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.25rem; }

    .wiz-sa-cell select { background: var(--bg3); border: 1px solid var(--bg4); border-radius: 4px; color: var(--text); font-size: 0.85rem; width: 100%; text-align: center; font-family: inherit; }

    .wiz-sa-final { font-size: 0.68rem; color: var(--green); font-weight: 600; margin-top: 0.15rem; min-height: 1em; }

    .wiz-man-grid { display: grid; grid-template-columns: repeat(6,1fr); gap: 0.4rem; margin-bottom: 0.5rem; }

    @media (max-width: 480px) { .wiz-man-grid { grid-template-columns: repeat(3,1fr); } }

    .wiz-man-cell { background: var(--bg2); border: 1px solid var(--bg3); border-radius: 8px; padding: 0.5rem 0.25rem; text-align: center; }

    .wiz-man-cell .man-label { font-size: 0.58rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.25rem; }

    .wiz-man-cell input { background: var(--bg3); border: 1px solid var(--bg4); border-radius: 4px; color: var(--text); font-size: 0.9rem; font-weight: 700; width: 100%; text-align: center; padding: 0.2rem; font-family: inherit; }

    .wiz-man-final { font-size: 0.68rem; color: var(--green); font-weight: 600; margin-top: 0.15rem; min-height: 1em; }

    .wiz-combat-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.5rem; margin-bottom: 0.75rem; }

    .wiz-note { font-size: 0.72rem; color: var(--text2); font-style: italic; margin-top: 0.5rem; }

    .wiz-asi-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-top: 0.5rem; }

    .wiz-asi-row select { background: var(--bg2); border: 1px solid var(--bg4); border-radius: 8px; color: var(--text); padding: 0.45rem 0.6rem; font-size: 0.85rem; font-family: inherit; width: 100%; }

    /* ── Fae Contract (parchment) ──────────────────────────────── */

    .fae-contract { background:#f4e4c1; color:#2c1810; padding:28px 22px; position:relative; border:6px double #8b4513; font-family:Georgia,serif; font-size:0.81rem; line-height:1.85; }

    .fae-contract::before { content:""; position:absolute; top:8px; left:8px; right:8px; bottom:8px; border:1px solid rgba(139,69,19,0.3); pointer-events:none; }

    .fae-title    { font-size:1.35rem; color:#4a0e0e; text-transform:uppercase; letter-spacing:3px; font-weight:bold; text-align:center; margin-bottom:0.2rem; }

    .fae-subtitle { font-style:italic; color:#6b4423; font-size:0.82rem; text-align:center; }

    .fae-hdr-rule { border:none; border-bottom:3px double #8b4513; margin:0.7rem 0 1.1rem; }

    .fae-drop-cap { float:left; font-size:3.4em; line-height:0.78; padding-right:7px; color:#4a0e0e; font-weight:bold; font-family:'Times New Roman',serif; }

    .fae-body p   { margin-bottom:14px; text-indent:32px; }

    .fae-body p:first-of-type { text-indent:0; }

    .fae-ambig    { border-bottom:1px dotted #8b4513; cursor:help; position:relative; }

    .fae-ambig:hover { color:#8b0000; }

    .fae-ambig::after { content:attr(data-h); position:absolute; bottom:110%; left:0; background:#2c1810; color:#f4e4c1; padding:4px 9px; font-size:0.75em; white-space:nowrap; opacity:0; transition:opacity 0.25s; pointer-events:none; border:1px solid #8b4513; z-index:20; border-radius:4px; }

    .fae-ambig:hover::after { opacity:1; }

    .fae-mirror   { display:inline-block; transform:scaleX(-1); color:#8b4513; font-style:italic; }

    .fae-updown   { display:inline-block; transform:rotate(180deg); font-size:0.9em; color:#5d4037; }

    .fae-fade     { background:linear-gradient(to right,#2c1810 0%,transparent 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }

    .fae-fl       { color:#8b0000; font-weight:bold; }

    .fae-hidden-p { color:#f4e4c1; }

    .fae-maze     { border:2px solid #8b4513; padding:14px; margin:18px 0; background:repeating-linear-gradient(0deg,transparent,transparent 23px,rgba(139,69,19,0.07) 24px); position:relative; }

    .fae-maze::before { content:"FIND THE PATH"; position:absolute; top:-10px; left:14px; background:#f4e4c1; padding:0 8px; font-size:0.72em; color:#8b4513; }

    .fae-mp       { background:rgba(139,0,0,0.14); font-weight:bold; color:#4a0e0e; }

    .fae-ws       { display:inline-grid; grid-template-columns:repeat(7,1.55em); gap:1px; font-family:monospace; font-size:0.8em; line-height:1.45; color:#5d4037; padding:11px; background:rgba(139,69,19,0.05); border:1px dashed #8b4513; text-align:center; }

    .fae-ws .wh   { background:rgba(139,0,0,0.17); font-weight:bold; color:#8b0000; }

    .fae-tbl      { width:100%; border-collapse:collapse; margin:16px 0; font-size:0.82em; }

    .fae-tbl th   { background:#4a0e0e; color:#f4e4c1; padding:9px 11px; text-align:left; font-weight:normal; text-transform:uppercase; letter-spacing:1.5px; }

    .fae-tbl td   { padding:9px 11px; border-bottom:1px solid rgba(139,69,19,0.22); vertical-align:top; }

    .fae-tbl tr:nth-child(even) td { background:rgba(139,69,19,0.04); }

    .fae-tbl .fa  { text-align:right; font-family:monospace; color:#4a0e0e; font-weight:bold; }

    .fae-tbl .fm  { font-size:0.7em; color:#6b4423; display:block; font-style:italic; margin-top:2px; }

    .fae-tbl .ftr td { background:#4a0e0e; color:#f4e4c1; font-weight:bold; }

    .fae-tbl .ftr .fa { color:#f4e4c1; }

    .fae-sig      { margin-top:24px; display:grid; grid-template-columns:1fr 1fr; gap:22px; }

    .fae-sig-box  { border:2px solid #8b4513; padding:14px; position:relative; min-height:80px; }

    .fae-sig-box::before { content:attr(data-label); position:absolute; top:-10px; left:10px; background:#f4e4c1; padding:0 7px; font-size:0.68em; color:#8b4513; }

    .fae-sig-line { border-bottom:2px solid #4a0e0e; height:32px; margin-top:18px; position:relative; }

    .fae-sig-line::after { content:attr(data-trap); position:absolute; bottom:-15px; left:0; font-size:0.58em; color:rgba(74,14,14,0.38); font-style:italic; }

    .fae-notary   { margin-top:18px; text-align:center; font-size:0.62em; color:rgba(139,69,19,0.4); }

    .fae-footer-c { margin-top:20px; text-align:center; font-size:0.75em; color:#6b4423; border-top:1px solid rgba(139,69,19,0.3); padding-top:14px; }

    /* old helpers kept for non-fae use */

    .contract-initial { font-size:1.25em; font-weight:800; color:var(--gold); font-family:serif; line-height:1; display:inline; }

    .hidden-cost      { color:var(--bg0); }

    .contract-line    { display:block; margin-bottom:0.5rem; line-height:1.6; font-size:0.79rem; color:var(--text2); }

    .contract-line .contract-initial { margin-right:1px; }

    .wiz-ddid-reveal { font-family: monospace; font-size: 1.4rem; font-weight: 800; color: var(--gold); background: var(--bg2); border: 1px solid rgba(255,215,0,0.3); border-radius: 10px; padding: 1rem; text-align: center; letter-spacing: 0.12em; margin: 0.75rem 0; }

    /* ═══════════════════════════════════════════════════════════

       NEW FEATURES CSS

       ═══════════════════════════════════════════════════════════ */

    /* ── Parchment Theme for D&D Flavor ── */

    .parchment {

      background: linear-gradient(135deg, #f4e4c1 0%, #e8d4a8 100%);

      border: 3px double #8b4513;

      border-radius: 8px;

      color: #2c1810;

      position: relative;

    }

    .parchment::before {

      content: "";

      position: absolute;

      top: 6px; left: 6px; right: 6px; bottom: 6px;

      border: 1px solid rgba(139,69,19,0.3);

      pointer-events: none;

      border-radius: 4px;

    }

    .parchment-title {

      font-family: Georgia, serif;

      color: #4a0e0e;

      text-transform: uppercase;

      letter-spacing: 2px;

      border-bottom: 2px solid #8b4513;

      padding-bottom: 0.5rem;

    }

    /* ── Magic Item Rarities ── */

    .item-common { border-color: #9e9e9e; box-shadow: 0 0 5px rgba(158,158,158,0.3); }

    .item-uncommon { border-color: #4caf50; box-shadow: 0 0 8px rgba(76,175,80,0.4); }

    .item-rare { border-color: #2196f3; box-shadow: 0 0 12px rgba(33,150,243,0.5); }

    .item-very-rare { border-color: #9c27b0; box-shadow: 0 0 15px rgba(156,39,176,0.5); }

    .item-legendary { border-color: #ff9800; box-shadow: 0 0 20px rgba(255,152,0,0.6); animation: legendary-pulse 2s infinite; }

    .item-artifact { border-color: #f44336; box-shadow: 0 0 25px rgba(244,67,54,0.7); animation: artifact-glow 1.5s infinite; }

    @keyframes legendary-pulse {

      0%, 100% { box-shadow: 0 0 20px rgba(255,152,0,0.6); }

      50% { box-shadow: 0 0 30px rgba(255,152,0,0.9); }

    }

    @keyframes artifact-glow {

      0%, 100% { box-shadow: 0 0 25px rgba(244,67,54,0.7); }

      50% { box-shadow: 0 0 40px rgba(244,67,54,1); }

    }

    /* ── Inventory Grid ── */

    .inventory-grid {

      display: grid;

      grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));

      gap: 0.5rem;

      padding: 0.75rem;

      background: var(--bg2);

      border-radius: 8px;

      min-height: 200px;

    }

    .inv-slot {

      aspect-ratio: 1;

      background: var(--bg3);

      border: 2px solid var(--bg4);

      border-radius: 8px;

      display: flex;

      flex-direction: column;

      align-items: center;

      justify-content: center;

      cursor: pointer;

      transition: all 0.2s;

      position: relative;

      font-size: 1.5rem;

    }

    .inv-slot:hover { border-color: var(--gold); transform: scale(1.05); }

    .inv-slot .item-qty {

      position: absolute;

      bottom: 2px;

      right: 4px;

      font-size: 0.65rem;

      font-weight: 700;

      background: var(--bg0);

      padding: 0 4px;

      border-radius: 4px;

    }

    .inv-slot .item-weight {

      position: absolute;

      top: 2px;

      left: 4px;

      font-size: 0.55rem;

      color: var(--text2);

    }

    .inv-equipped {

      position: absolute;

      top: -4px;

      right: -4px;

      background: var(--gold);

      color: var(--bg0);

      font-size: 0.6rem;

      padding: 2px 4px;

      border-radius: 4px;

      font-weight: 700;

    }

    .encumbrance-bar {

      height: 6px;

      background: var(--bg3);

      border-radius: 3px;

      overflow: hidden;

      margin-top: 0.5rem;

    }

    .encumbrance-fill {

      height: 100%;

      background: var(--green);

      transition: width 0.3s, background 0.3s;

    }

    .enc-tick {

      position: absolute;

      top: 0;

      width: 1px;

      height: 100%;

      background: var(--bg1);

      opacity: 0.7;

      pointer-events: auto;

    }

    /* ── Spellbook ── */

    .spell-slot {

      display: inline-flex;

      align-items: center;

      justify-content: center;

      width: 28px;

      height: 28px;

      border-radius: 50%;

      border: 2px solid var(--bg4);

      background: var(--bg2);

      cursor: pointer;

      transition: all 0.15s;

      font-size: 0.7rem;

    }

    .spell-slot.filled {

      background: var(--blue);

      border-color: var(--blue);

      color: var(--bg0);

    }

    .spell-slot.used {

      background: transparent;

      border-color: var(--red);

      color: var(--red);

      text-decoration: line-through;

    }

    .spell-card {

      background: var(--bg2);

      border: 1px solid var(--bg3);

      border-radius: 8px;

      padding: 0.75rem;

      margin-bottom: 0.5rem;

      cursor: pointer;

      transition: all 0.15s;

    }

    .spell-card:hover { border-color: var(--blue); }

    .spell-card.prepared {

      border-color: var(--blue);

      background: rgba(91,155,213,0.1);

    }

    .spell-level-group {

      margin-bottom: 1rem;

    }

    .spell-level-header {

      font-size: 0.75rem;

      color: var(--text2);

      text-transform: uppercase;

      letter-spacing: 0.1em;

      margin-bottom: 0.5rem;

      display: flex;

      align-items: center;

      gap: 0.5rem;

    }

    /* ── Death Saves ── */

    .death-save-box {

      display: flex;

      align-items: center;

      gap: 0.5rem;

      padding: 0.75rem;

      background: rgba(224,85,85,0.1);

      border: 1px solid rgba(224,85,85,0.3);

      border-radius: 8px;

      margin-bottom: 0.75rem;

    }

    .death-save-title {

      font-size: 0.8rem;

      color: var(--red);

      font-weight: 700;

      text-transform: uppercase;

    }

    .death-save-track {

      display: flex;

      gap: 0.35rem;

    }

    .death-dot {

      width: 20px;

      height: 20px;

      border-radius: 50%;

      border: 2px solid var(--bg4);

      cursor: pointer;

      transition: all 0.15s;

    }

    .death-dot.success { background: var(--green); border-color: var(--green); }

    .death-dot.fail { background: var(--red); border-color: var(--red); }

    .death-dot:hover { transform: scale(1.2); }

    /* ── Inspiration ── */

    /* Padding/icon size fluid (clamp()) 2026-07-25, same pass as
       .profile-header — single-use class (confirmed via grep), safe to
       scale in isolation. */
    .inspiration-toggle {

      display: inline-flex;

      align-items: center;

      gap: clamp(0.4rem, 0.35rem + 0.3vw, 0.6rem);

      padding: clamp(0.3rem, 0.25rem + 0.3vw, 0.5rem) clamp(0.55rem, 0.45rem + 0.5vw, 0.9rem);

      background: var(--bg2);

      border: 2px solid var(--bg4);

      border-radius: 20px;

      cursor: pointer;

      transition: all 0.2s;

      /* .vitals-grid sits Inspiration in the same grid row as the stat-
         box column (#p-combat-stats) — that column grew from 4 tiles to
         7 and now wraps 3 lines tall, so the row track itself is much
         taller than Inspiration's own compact content. .vitals-grid uses
         align-items:start (so the taller/shorter columns don't stretch),
         which leaves Inspiration pinned to the top of that tall row with
         empty space below it. Centering it in its own cell instead reads
         as intentional placement rather than a gap. */
      align-self: center;

    }

    .inspiration-toggle.active {

      background: rgba(255,215,0,0.15);

      border-color: var(--gold);

      box-shadow: 0 0 15px rgba(255,215,0,0.4);

    }

    .inspiration-toggle i {

      font-size: clamp(0.95rem, 0.85rem + 0.4vw, 1.3rem);

      color: var(--text2);

      transition: all 0.2s;

    }

    .inspiration-toggle.active i {

      color: var(--gold);

      transform: scale(1.2);

    }

    /* ── Conditions ── */

    .conditions-bar {

      display: flex;

      flex-wrap: wrap;

      gap: 0.35rem;

      margin-top: 0.5rem;

    }

    .condition-badge {

      display: inline-flex;

      align-items: center;

      gap: 0.3rem;

      padding: 0.2rem 0.5rem;

      border-radius: 4px;

      font-size: 0.72rem;

      font-weight: 600;

      cursor: pointer;

      transition: all 0.15s;

    }

    .condition-blinded { background: rgba(100,100,100,0.3); border: 1px solid #666; }

    .condition-charmed { background: rgba(255,105,180,0.2); border: 1px solid #ff69b4; }

    .condition-deafened { background: rgba(150,150,150,0.3); border: 1px solid #999; }

    .condition-frightened { background: rgba(128,0,128,0.2); border: 1px solid #800080; }

    .condition-grappled { background: rgba(139,69,19,0.2); border: 1px solid #8b4513; }

    .condition-incapacitated { background: rgba(128,128,128,0.3); border: 1px solid #808080; }

    .condition-invisible { background: rgba(173,216,230,0.2); border: 1px solid #add8e6; }

    .condition-paralyzed { background: rgba(255,255,0,0.2); border: 1px solid #ffff00; }

    .condition-petrified { background: rgba(128,128,128,0.4); border: 1px solid #696969; }

    .condition-poisoned { background: rgba(0,128,0,0.2); border: 1px solid #008000; }

    .condition-prone { background: rgba(210,180,140,0.2); border: 1px solid #d2b48c; }

    .condition-restrained { background: rgba(139,0,0,0.2); border: 1px solid #8b0000; }

    .condition-stunned { background: rgba(255,165,0,0.2); border: 1px solid #ffa500; }

    .condition-unconscious { background: rgba(0,0,0,0.3); border: 1px solid #000; }

    

    /* Active/Glowing Conditions */

    .condition-active {

      box-shadow: 0 0 12px currentColor, 0 0 20px currentColor;

      transform: scale(1.05);

      font-weight: 700;

    }

    /* Conditions modal cards are full-width tiles with a rules blurb,
       not the compact Hero pills. The .condition-active glow + 1.05
       scale made the active cards overlap their neighbors and cover
       neighbor text. Strip the scale and halo when the class is also
       a .condition-card; use a clean inset border as the active cue
       instead. Per-condition background tints still apply so each
       card keeps its accent color. */
    .condition-card.condition-active {

      transform: none !important;

      box-shadow: inset 0 0 0 2px currentColor !important;

      font-weight: 600;

    }

    /* Conditions modal tiles pick up their accent color and border
       from the per-condition class (.condition-blinded, etc.). The
       previous rule added its own border, which took specificity
       precedence over the per-condition borders and flattened every
       tile to the same grey. Keep only transition + interaction
       polish here and let .condition-X own the visual. */
    .condition-card.condition-blinded,
    .condition-card.condition-charmed,
    .condition-card.condition-deafened,
    .condition-card.condition-frightened,
    .condition-card.condition-grappled,
    .condition-card.condition-incapacitated,
    .condition-card.condition-invisible,
    .condition-card.condition-paralyzed,
    .condition-card.condition-petrified,
    .condition-card.condition-poisoned,
    .condition-card.condition-prone,
    .condition-card.condition-restrained,
    .condition-card.condition-stunned,
    .condition-card.condition-unconscious {
      color: var(--text);
      transition: transform 0.1s, filter 0.15s, box-shadow 0.15s, opacity 0.15s;
    }
    /* Was button.condition-card — the tile became a <div> 2026-07-20 so
       it could host a real nested <button> for the "?" info toggle
       (button-in-button isn't valid HTML). Dropped the tag requirement
       so hover/press feedback still applies. */
    .condition-card:hover:not(:disabled) {
      filter: saturate(1.3) brightness(1.1);
    }
    .condition-card:active {
      transform: scale(0.97) !important;
    }

    .condition-blinded.condition-active { box-shadow: 0 0 12px #666, 0 0 20px rgba(100,100,100,0.5); }

    .condition-charmed.condition-active { box-shadow: 0 0 12px #ff69b4, 0 0 20px rgba(255,105,180,0.5); background: rgba(255,105,180,0.4); }

    .condition-deafened.condition-active { box-shadow: 0 0 12px #999, 0 0 20px rgba(150,150,150,0.5); }

    .condition-frightened.condition-active { box-shadow: 0 0 12px #800080, 0 0 20px rgba(128,0,128,0.5); background: rgba(128,0,128,0.4); }

    .condition-grappled.condition-active { box-shadow: 0 0 12px #8b4513, 0 0 20px rgba(139,69,19,0.5); background: rgba(139,69,19,0.4); }

    .condition-incapacitated.condition-active { box-shadow: 0 0 12px #808080, 0 0 20px rgba(128,128,128,0.5); }

    .condition-invisible.condition-active { box-shadow: 0 0 12px #add8e6, 0 0 20px rgba(173,216,230,0.5); background: rgba(173,216,230,0.4); }

    .condition-paralyzed.condition-active { box-shadow: 0 0 12px #ffff00, 0 0 20px rgba(255,255,0,0.5); background: rgba(255,255,0,0.3); }

    .condition-petrified.condition-active { box-shadow: 0 0 12px #696969, 0 0 20px rgba(105,105,105,0.5); }

    .condition-poisoned.condition-active { box-shadow: 0 0 12px #008000, 0 0 20px rgba(0,128,0,0.5); background: rgba(0,128,0,0.4); }

    .condition-prone.condition-active { box-shadow: 0 0 12px #d2b48c, 0 0 20px rgba(210,180,140,0.5); background: rgba(210,180,140,0.4); }

    .condition-restrained.condition-active { box-shadow: 0 0 12px #8b0000, 0 0 20px rgba(139,0,0,0.5); background: rgba(139,0,0,0.4); }

    .condition-stunned.condition-active { box-shadow: 0 0 12px #ffa500, 0 0 20px rgba(255,165,0,0.5); background: rgba(255,165,0,0.4); }

    .condition-unconscious.condition-active { box-shadow: 0 0 12px #000, 0 0 20px rgba(0,0,0,0.5); background: rgba(0,0,0,0.5); color: #fff; }

    /* ── Achievement Badges ── */

    .achievement {

      display: flex;

      align-items: center;

      gap: 0.75rem;

      padding: 0.75rem;

      background: var(--bg2);

      border: 1px solid var(--bg3);

      border-radius: 8px;

      margin-bottom: 0.5rem;

      opacity: 0.5;

      transition: all 0.3s;

    }

    .achievement.unlocked {

      opacity: 1;

      border-color: var(--gold);

      background: rgba(255,215,0,0.05);

    }

    .achievement-icon {

      width: 40px;

      height: 40px;

      border-radius: 50%;

      display: flex;

      align-items: center;

      justify-content: center;

      font-size: 1.2rem;

      background: var(--bg3);

    }

    .achievement.unlocked .achievement-icon {

      background: linear-gradient(135deg, var(--gold) 0%, #ffed4e 100%);

      color: var(--bg0);

      box-shadow: 0 0 15px rgba(255,215,0,0.4);

    }

    .achievement-title {

      font-weight: 700;

      font-size: 0.9rem;

    }

    .achievement-desc {

      font-size: 0.75rem;

      color: var(--text2);

    }

    /* ── Level Up Modal ── */

    .level-up-banner {

      text-align: center;

      padding: 1.5rem;

      background: linear-gradient(135deg, rgba(255,215,0,0.2) 0%, rgba(255,215,0,0.05) 100%);

      border: 2px solid var(--gold);

      border-radius: 12px;

      margin-bottom: 1rem;

    }

    .level-up-number {

      font-size: 4rem;

      font-weight: 800;

      color: var(--gold);

      line-height: 1;

      text-shadow: 0 0 30px rgba(255,215,0,0.5);

    }

    .hp-roll-box {

      display: flex;

      align-items: center;

      justify-content: center;

      gap: 1rem;

      padding: 1rem;

      background: var(--bg2);

      border-radius: 8px;

      margin: 0.5rem 0;

    }

    .dice-roll-btn {

      width: 60px;

      height: 60px;

      border-radius: 12px;

      background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%);

      border: 2px solid #ff6b6b;

      color: white;

      font-size: 1.5rem;

      font-weight: 700;

      cursor: pointer;

      transition: all 0.2s;

      display: flex;

      align-items: center;

      justify-content: center;

    }

    .dice-roll-btn:hover { transform: scale(1.1); box-shadow: 0 0 20px rgba(196,30,58,0.5); }

    .dice-roll-btn:active { transform: scale(0.95); }

    .dice-roll-btn.rolling { animation: dice-shake 0.5s ease; }

    @keyframes dice-shake {

      0%, 100% { transform: rotate(0deg); }

      25% { transform: rotate(-15deg); }

      50% { transform: rotate(15deg); }

      75% { transform: rotate(-10deg); }

    }

    /* ── Reputation/Factions ── */

    .faction-row {

      display: flex;

      align-items: center;

      gap: 0.75rem;

      padding: 0.75rem;

      background: var(--bg2);

      border-radius: 8px;

      margin-bottom: 0.5rem;

    }

    .faction-icon {

      width: 36px;

      height: 36px;

      border-radius: 50%;

      display: flex;

      align-items: center;

      justify-content: center;

      font-size: 1rem;

    }

    .faction-name {

      flex: 1;

      font-weight: 600;

    }

    .faction-standing {

      font-size: 0.75rem;

      padding: 0.2rem 0.5rem;

      border-radius: 4px;

      font-weight: 600;

    }

    .standing-hostile { background: rgba(244,67,54,0.2); color: #f44336; }

    .standing-unfriendly { background: rgba(255,87,34,0.2); color: #ff5722; }

    .standing-indifferent { background: rgba(158,158,158,0.2); color: #9e9e9e; }

    .standing-friendly { background: rgba(76,175,80,0.2); color: #4caf50; }

    .standing-honored { background: rgba(33,150,243,0.2); color: #2196f3; }

    .standing-revered { background: rgba(156,39,176,0.2); color: #9c27b0; }

    .standing-exalted { background: rgba(255,215,0,0.2); color: var(--gold); }

    /* ── Tavern Chat ── */

    .chat-container {

      display: flex;

      flex-direction: column;

      height: 400px;

      background: var(--bg2);

      border-radius: 8px;

      overflow: hidden;

    }

    .chat-messages {

      flex: 1;

      overflow-y: auto;

      padding: 0.75rem;

      display: flex;

      flex-direction: column;

      gap: 0.5rem;

    }

    .chat-message {

      max-width: 80%;

      padding: 0.6rem 0.8rem;

      border-radius: 12px;

      font-size: 0.85rem;

      position: relative;

    }

    .chat-message.own {

      align-self: flex-end;

      background: rgba(255,215,0,0.15);

      border: 1px solid rgba(255,215,0,0.3);

    }

    .chat-message.other {

      align-self: flex-start;

      background: var(--bg3);

      border: 1px solid var(--bg4);

    }

    .chat-message.ic {

      font-style: italic;

      border-left: 3px solid var(--blue);

    }

    .chat-author {

      font-size: 0.7rem;

      font-weight: 700;

      color: var(--gold);

      margin-bottom: 0.2rem;

    }

    .chat-time {

      font-size: 0.65rem;

      color: var(--text2);

      margin-top: 0.2rem;

    }

    .chat-input-area {

      display: flex;

      gap: 0.5rem;

      padding: 0.75rem;

      background: var(--bg1);

      border-top: 1px solid var(--bg3);

    }

    .chat-input {

      flex: 1;

      background: var(--bg2);

      border: 1px solid var(--bg4);

      border-radius: 20px;

      padding: 0.5rem 1rem;

      color: var(--text);

      font-family: inherit;

    }

    .chat-input:focus { outline: none; border-color: var(--gold); }

    .ic-toggle {

      display: inline-flex;

      align-items: center;

      gap: 0.3rem;

      padding: 0.3rem 0.6rem;

      background: var(--bg2);

      border: 1px solid var(--bg4);

      border-radius: 12px;

      font-size: 0.72rem;

      cursor: pointer;

      transition: all 0.15s;

    }

    .ic-toggle.active {

      background: rgba(91,155,213,0.2);

      border-color: var(--blue);

      color: var(--blue);

    }

    /* ── Character Journal ── */

    .journal-entry {

      background: var(--bg2);

      border-left: 3px solid var(--gold);

      border-radius: 0 8px 8px 0;

      padding: 0.75rem;

      margin-bottom: 0.75rem;

    }

    .journal-date {

      font-size: 0.7rem;

      color: var(--text2);

      margin-bottom: 0.25rem;

    }

    .journal-title {

      font-weight: 700;

      color: var(--gold);

      margin-bottom: 0.4rem;

    }

    .journal-text {

      font-size: 0.85rem;

      line-height: 1.5;

      white-space: pre-wrap;

    }

    .journal-input {

      width: 100%;

      background: var(--bg2);

      border: 1px solid var(--bg4);

      border-radius: 8px;

      padding: 0.75rem;

      color: var(--text);

      font-family: inherit;

      resize: vertical;

      min-height: 100px;

    }

    /* ── DM Notes ── */

    .dm-notes-area {

      width: 100%;

      background: rgba(224,85,85,0.05);

      border: 1px solid rgba(224,85,85,0.2);

      border-radius: 8px;

      padding: 0.75rem;

      color: var(--text);

      font-family: inherit;

      resize: vertical;

      min-height: 80px;

      font-size: 0.85rem;

    }

    .dm-notes-area::placeholder { color: var(--text2); }

    .dm-only-badge {

      display: inline-flex;

      align-items: center;

      gap: 0.3rem;

      padding: 0.15rem 0.4rem;

      background: rgba(224,85,85,0.2);

      color: var(--red);

      font-size: 0.65rem;

      font-weight: 700;

      border-radius: 4px;

      text-transform: uppercase;

    }

    /* ── Relationship Map ── */

    .relationship-node {

      display: inline-flex;

      align-items: center;

      gap: 0.5rem;

      padding: 0.4rem 0.75rem;

      background: var(--bg2);

      border: 2px solid var(--bg4);

      border-radius: 20px;

      font-size: 0.8rem;

      cursor: pointer;

      transition: all 0.15s;

      margin: 0.25rem;

    }

    .relationship-node:hover { border-color: var(--gold); }

    .relationship-node.player { border-color: var(--blue); }

    .relationship-node.npc { border-color: var(--purple); }

    .relationship-node.villain { border-color: var(--red); }

    .rel-status {

      width: 8px;

      height: 8px;

      border-radius: 50%;

    }

    .rel-ally { background: var(--green); }

    .rel-friendly { background: #8bc34a; }

    .rel-neutral { background: var(--text2); }

    .rel-unfriendly { background: #ff9800; }

    .rel-enemy { background: var(--red); }

    /* ── Profile Edit Modal ── */

    .edit-section {

      margin-bottom: 1.25rem;

      padding-bottom: 1rem;

      border-bottom: 1px solid var(--bg3);

    }

    .edit-section:last-child { border-bottom: none; }

    .edit-section-title {

      font-size: 0.8rem;

      color: var(--gold);

      text-transform: uppercase;

      letter-spacing: 0.1em;

      margin-bottom: 0.75rem;

      display: flex;

      align-items: center;

      gap: 0.5rem;

    }

    /* ── Password Reset Flow ── */

    .reset-steps {

      display: flex;

      justify-content: center;

      gap: 1rem;

      margin-bottom: 1.5rem;

    }

    .reset-step {

      width: 32px;

      height: 32px;

      border-radius: 50%;

      background: var(--bg3);

      border: 2px solid var(--bg4);

      display: flex;

      align-items: center;

      justify-content: center;

      font-size: 0.85rem;

      font-weight: 700;

      color: var(--text2);

    }

    .reset-step.active {

      background: var(--gold);

      border-color: var(--gold);

      color: var(--bg0);

    }

    .reset-step.completed {

      background: var(--green);

      border-color: var(--green);

      color: white;

    }

    /* ── XP Tracker ── */

    /* ── Session Rating Wizard ── */

    .sr-aspect-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(130px,1fr)); gap:0.55rem; margin-bottom:0.5rem; }

    .sr-aspect-btn { background:var(--bg2); border:2px solid var(--bg3); border-radius:10px; padding:0.65rem 0.4rem; text-align:center; cursor:pointer; transition:all 0.15s; user-select:none; }

    .sr-aspect-btn:hover { border-color:var(--gold); }

    .sr-aspect-btn.selected { border-color:var(--gold); background:rgba(255,215,0,0.1); }

    .sr-aspect-btn .sa-icon { font-size:1.3rem; margin-bottom:0.25rem; }

    .sr-aspect-btn .sa-label { font-size:0.8rem; font-weight:700; }

    .sr-aspect-btn .sa-desc { font-size:0.65rem; color:var(--text2); margin-top:0.1rem; }

    .sr-peer-block { background:var(--bg2); border:1px solid var(--bg3); border-radius:10px; padding:0.7rem; margin-bottom:0.55rem; }

    .sr-peer-name { font-weight:700; font-size:0.88rem; display:flex; align-items:center; gap:0.4rem; margin-bottom:0.55rem; }

    .sr-peer-aspect { display:flex; align-items:center; justify-content:space-between; margin-bottom:0.35rem; font-size:0.8rem; }

    .sr-aspect-lbl { color:var(--text2); flex:1; }

    .star-row { display:flex; gap:0.2rem; }

    .star-btn { background:none; border:none; font-size:1.1rem; cursor:pointer; color:var(--bg3); padding:0; line-height:1; transition:color 0.1s; }

    .star-btn.lit { color:var(--gold); }

    .star-btn:hover { color:var(--gold); }

    .sr-progress-row { display:flex; align-items:center; gap:0.6rem; font-size:0.8rem; margin-bottom:0.4rem; }

    .sr-progress-bar { flex:1; height:6px; background:var(--bg3); border-radius:4px; overflow:hidden; }

    .sr-progress-fill { height:100%; background:var(--gold); border-radius:4px; transition:width 0.4s; }

    .xp-bar-container {

      position: relative;

      height: 24px;

      background: var(--bg3);

      border-radius: 12px;

      overflow: hidden;

      margin: 0.5rem 0;

    }

    .xp-bar-fill {

      height: 100%;

      background: linear-gradient(90deg, var(--purple) 0%, var(--blue) 100%);

      transition: width 0.5s ease;

      display: flex;

      align-items: center;

      justify-content: flex-end;

      padding-right: 0.5rem;

    }

    .xp-text {

      position: absolute;

      top: 50%;

      left: 50%;

      transform: translate(-50%, -50%);

      font-size: 0.75rem;

      font-weight: 700;

      color: var(--text);

      text-shadow: 0 1px 2px rgba(0,0,0,0.5);

      white-space: nowrap;

    }

    /* ── Downtime Activity Cards ── */

    .downtime-card {

      background: var(--bg2);

      border: 1px solid var(--bg3);

      border-radius: 8px;

      padding: 0.75rem;

      cursor: pointer;

      transition: all 0.15s;

    }

    .downtime-card:hover { border-color: var(--gold); }

    .downtime-card.active {

      border-color: var(--gold);

      background: rgba(255,215,0,0.05);

    }

    .downtime-icon {

      font-size: 1.5rem;

      margin-bottom: 0.4rem;

    }

    .downtime-name {

      font-weight: 700;

      font-size: 0.85rem;

    }

    .downtime-desc {

      font-size: 0.72rem;

      color: var(--text2);

      margin-top: 0.2rem;

    }

    /* ── Portrait Upload ── */

    .portrait-upload {

      width: 120px;

      height: 120px;

      border-radius: 50%;

      border: 3px dashed var(--bg4);

      background: var(--bg2);

      display: flex;

      flex-direction: column;

      align-items: center;

      justify-content: center;

      cursor: pointer;

      transition: all 0.2s;

      overflow: hidden;

      position: relative;

    }

    .portrait-upload:hover { border-color: var(--gold); }

    .portrait-upload.has-image { border-style: solid; border-color: var(--gold); }

    .portrait-upload img {

      width: 100%;

      height: 100%;

      object-fit: cover;

    }

    .portrait-overlay {

      position: absolute;

      inset: 0;

      background: rgba(0,0,0,0.6);

      display: flex;

      align-items: center;

      justify-content: center;

      opacity: 0;

      transition: opacity 0.2s;

    }

    .portrait-upload:hover .portrait-overlay { opacity: 1; }

    /* Hero-tab inline avatar upload — hover the avatar to reveal a
       camera overlay hinting it's clickable. */
    #p-profile-avatar:hover #p-avatar-upload-overlay { opacity: 1; }

    /* ── Toast animations ── */

    @keyframes achievement-unlock {

      0% { transform: scale(0.8); opacity: 0; }

      50% { transform: scale(1.1); }

      100% { transform: scale(1); opacity: 1; }

    }

    .toast.achievement-toast {

      border-color: var(--gold);

      background: linear-gradient(135deg, rgba(255,215,0,0.15) 0%, var(--bg2) 100%);

      animation: achievement-unlock 0.5s ease;

    }

  

/*  Tab Navigation  */

/* border-bottom kept at 2px for the existing sticky-offset math (52
   topbar + 54 tabs = 106 subnav top) but tinted to --bg1 so it
   visually merges with the bar above it and the subnav below. The
   previous --bg3 border read as a darker hairline between the two
   strips — what users were seeing as "the gap." */
.dash-tabs{display:flex;background:var(--bg1);border-bottom:2px solid var(--bg1);position:sticky;top:calc(52px + var(--sat));z-index:50;overflow:hidden;}

/* Inactive tabs use --text (the bright foreground) instead of the
   muted --text2 so the tab labels read clearly against --bg1. The
   active tab still flips to gold + gold underline so it remains the
   strongest visual element. Pre-fix was reported as "white on gray
   makes tabs hard to notice." */
.dash-tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0.2rem;padding:0.55rem 0.25rem;min-height:52px;border:none;background:transparent;color:var(--text);opacity:0.78;font-size:0.65rem;font-weight:600;letter-spacing:0.03em;cursor:pointer;border-bottom:2px solid transparent;transition:color 0.15s,opacity 0.15s,border-color 0.15s,background 0.15s,box-shadow 0.15s,text-shadow 0.15s;-webkit-tap-highlight-color:transparent;}

.dash-tab i{font-size:1.1rem;}

.dash-tab:hover{opacity:1;background:rgba(255,215,0,0.04);box-shadow:inset 0 -3px 8px -4px rgba(255,215,0,0.35);}

.dash-tab.active{color:var(--gold);opacity:1;border-bottom-color:var(--gold);box-shadow:inset 0 -3px 10px -4px rgba(255,215,0,0.45);text-shadow:0 0 8px rgba(255,215,0,0.22);}

.dash-tab:active{background:rgba(255,215,0,0.08);}

.dash-panel{display:none;}

/* ── DM Cockpit (Board tab) layout ─────────────────────────
   The Board panel is the active-DMing screen, not a kitchen
   sink. Always-visible cluster: header, maps, shift, campaign
   tab selector, init tracker, live status, DI card, recent
   activity. Everything else (including Player Notes) lives
   behind the #dm-board-subnav button row,
   grouped into .dm-board-group panes (data-group="overview" /
   "approvals" / "encounters" / "combatlog" / "xplog"). Only the
   group matching #dmpanel-board's [data-active-group] is shown,
   so picking a button surfaces just that content at a glance
   instead of one long scroll of every secondary tool at once. */
#dmpanel-board.active {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
/* Explicit ordering — makes combat surfaces sit right under
   the cockpit header, with notes + activity below them. */
#dm-cockpit-header           { order: 0; }
#dm-shift-widget             { order: 1; }
#dm-board-camp-tabs          { order: 2; }
#dm-board-subnav             { order: 3; }
.dm-board-group              { order: 4; display: none; }
#dmpanel-board[data-active-group="overview"]   .dm-board-group[data-group="overview"],
#dmpanel-board[data-active-group="approvals"]  .dm-board-group[data-group="approvals"],
#dmpanel-board[data-active-group="encounters"] .dm-board-group[data-group="encounters"],
#dmpanel-board[data-active-group="combatlog"]  .dm-board-group[data-group="combatlog"],
#dmpanel-board[data-active-group="xplog"]      .dm-board-group[data-group="xplog"] {
  display: block;
}
/* Per-campaign quest body still needs its own hide when the DM has
   no campaigns at all, layered on top of the group visibility above. */
.dm-board-group.dm-camp-empty { display: none !important; }
/* #dm-kpis is also .kpi-grid (needs display:grid, not the generic
   block the rule above gives every other group pane). */
#dmpanel-board[data-active-group="overview"] #dm-kpis.dm-board-group[data-group="overview"] {
  display: grid;
}
#dmboard-card-init           { order: 5; }
#dmboard-card-live           { order: 6; }
#dm-di-card                  { order: 7; }
#dmboard-card-notes          { order: 8; }
#dmboard-card-activity       { order: 9; }
@media (min-width: 1100px) {
  #dmpanel-board.active {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    gap: 0.6rem 0.7rem;
    align-items: start;
  }
  /* Top row: span both columns. */
  #dm-cockpit-header,
  #dm-shift-widget,
  #dm-board-camp-tabs,
  #dm-board-subnav,
  .dm-board-group {
    grid-column: 1 / -1;
  }
  /* Left column (combat/HP cluster) */
  #dmboard-card-init,
  #dmboard-card-live,
  #dm-di-card {
    grid-column: 1;
  }
  /* Right column (action / notes / activity) */
  #dmboard-card-notes,
  #dmboard-card-activity {
    grid-column: 2;
  }
}

.dash-panel.active{display:block;}

/* Persistent top-bar stat pills — compact HP / AC chips always on
   the player topbar. On narrow widths they drop the rest buttons
   and show just the two pills so the bar doesn't overflow. */
.topbar-stat{display:inline-flex;align-items:center;gap:0.25rem;font-size:0.72rem;font-weight:700;padding:0.2rem 0.55rem;border-radius:999px;background:var(--bg3);border:1px solid var(--bg4);white-space:nowrap;}
.topbar-stat.hp{color:var(--red);border-color:rgba(224,96,96,0.35);}
.topbar-stat.ac{color:var(--blue);border-color:rgba(106,164,224,0.35);}
/* .topbar-rest rules removed — the buttons themselves were
   removed from the player topbar markup; rests still live in
   the Hero tab's REST section. */
@media (max-width:600px){
  .topbar-stat{font-size:0.66rem;padding:0.15rem 0.45rem;}
}

/* Contacts filter pills — lightweight segmented filter on the Guild
   > Party sub-tab's unified contacts directory. */
/* .contacts-pill / .contact-row styles removed — Unified Contacts
   directory section was deleted; styles were only used by it. */

/* Wealth sticky summary — 4-cell strip at the top of the Wealth
   panel. Pins under the tab bar (no sub-nav on Wealth, so top:104px). */
.rw-collapsible.rw-collapsed .rw-collapsible-body{display:none;}
.rw-collapsible.rw-collapsed .rw-collapsible-caret{transform:rotate(-90deg);}
.rw-collapsible-head:hover .rw-collapsible-caret{color:var(--gold);}

.cashflow-range-btn{background:var(--bg2);color:var(--text2);border:1px solid var(--bg3);border-radius:999px;padding:0.25rem 0.7rem;font-size:0.7rem;font-weight:600;cursor:pointer;}
.cashflow-range-btn.active{background:rgba(76,175,80,0.14);color:var(--green);border-color:rgba(76,175,80,0.4);}
.cashflow-range-btn:hover:not(.active){border-color:var(--bg4);color:var(--text);}

/* Pinned flush against the bottom of the Wealth sub-nav. Math:
   topbar (52) + dash-tabs (54) + dash-subnav (~42) = 148. Previous
   value of 158 left a 10px gap between the subnav and the summary
   strip, which the user reported as overlap/disconnection (the
   summary appeared to float instead of latching onto the header
   stack). Background bumped from --bg0 to --bg1 so it merges with
   the bar above instead of showing a colour seam. */
/* Two rows now (2026-07-20): .ws-kpi-row (the original grid of 3 cells)
   + .ws-actions-row (quick-action buttons, moved up here from an
   un-pinned row further down the page — same "buttons live in the
   sticky banner" pattern as Hero's vitals-bar). */
.wealth-sticky-summary{position:sticky;top:calc(148px + var(--sat));z-index:47;display:flex;flex-direction:column;gap:0.5rem;background:var(--bg1);padding:0.65rem 0.3rem 0.8rem;margin:0 -0.2rem 0.65rem;border-bottom:1px solid var(--bg3);box-shadow:0 6px 16px -12px rgba(0,0,0,0.6);}
.wealth-sticky-summary .ws-kpi-row{display:grid;grid-template-columns:repeat(3,1fr);gap:0.5rem;}
.wealth-sticky-summary .ws-actions-row{display:flex;justify-content:space-between;align-items:center;gap:0.4rem;flex-wrap:wrap;}
.wealth-sticky-summary .ws-cell{background:var(--bg2);border:1px solid var(--bg3);border-radius:8px;padding:0.55rem 0.5rem;text-align:center;min-width:0;transition:background 0.15s,border-color 0.15s;}
.wealth-sticky-summary .ws-label{font-size:0.62rem;color:var(--text2);text-transform:uppercase;letter-spacing:0.07em;}
.wealth-sticky-summary .ws-value{font-weight:800;font-size:1.15rem;color:var(--text);margin-top:0.18rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.wealth-sticky-summary .ws-value.gold{color:var(--gold);}
.wealth-sticky-summary .ws-value.red{color:var(--red);}
.wealth-sticky-summary .ws-value.green{color:var(--green);}
/* Tint the whole cell to match its value's color, not just the number —
   makes the strip read as a real KPI band instead of four gray boxes with
   colored text. Rep Tier is deliberately left untinted: its text color is
   set dynamically by JS per faction tier (repRankColor()), so a fixed
   background would fight whatever color that ends up being. */
.wealth-sticky-summary .ws-cell:has(.ws-value.green){background:rgba(76,175,80,0.08);border-color:rgba(76,175,80,0.3);}
.wealth-sticky-summary .ws-cell:has(.ws-value.red){background:rgba(224,85,85,0.08);border-color:rgba(224,85,85,0.3);}
.wealth-sticky-summary .ws-cell:has(.ws-value.gold){background:rgba(255,215,0,0.08);border-color:rgba(255,215,0,0.3);}
@media (max-width:600px){
  /* Mobile math: topbar (52) + dash-tabs (50) + dash-subnav (~42)
     = 144. Subnav top is 102 on mobile (corrected with the
     dash-tabs gap fix), so 144 keeps the summary flush. */
  .wealth-sticky-summary{top:calc(144px + var(--sat));gap:0.3rem;padding:0.3rem 0.1rem;}
  .wealth-sticky-summary .ws-kpi-row{grid-template-columns:repeat(3,1fr);gap:0.25rem;}
  .wealth-sticky-summary .ws-cell{padding:0.25rem 0.2rem;}
  .wealth-sticky-summary .ws-value{font-size:0.78rem;}
  .wealth-sticky-summary .ws-label{font-size:0.52rem;}
}

/* Research Library — two-column layout matching dungeon_dash_study.html's
   own .two-col (Library left, Active Training right), restored 2026-07-21
   after a brief stacked-layout detour. No align-items override — the
   default stretch makes .rlib-col-side match the taller left column's
   row height, which is what gives #research-active-training's sticky
   positioning room to actually stick while the (usually much taller)
   subject grid scrolls past. */
.rlib-two-col{display:grid;grid-template-columns:1fr 300px;gap:1rem;}
@media (max-width:900px){.rlib-two-col{grid-template-columns:1fr;}}

/* Active Training pinned while scrolling through the subject grid.
   Sits at the same page position dungeon_dash_study.html's own
   right-hand panel used to (which needed no sticky — it's this file's
   full-page scroll context that makes it necessary now). Reuses
   .wealth-sticky-summary's top:148px math: it sits directly under
   #research-content-subnav (a .hero-subnav, sticky at top:106px), plus
   that subnav's own ~42px rendered height. */
#research-active-training{position:sticky;top:calc(148px + var(--sat));background:var(--bg1);padding-bottom:0.6rem;}

/* Aggregate tab sub-nav — appears directly under the main tab bar
   when Adventuring or Guild is the active parent. Pinned at top:106
   (desktop) / top:100 (mobile) below — flush with the dash-tabs
   strip (52px topbar + 54px tabs incl. 2px border). The previous
   +2px buffer read as a dark slit of page-bg between the two
   rows while scrolling; touching them edge-to-edge removes the
   slit without reintroducing overlap. */
.dash-subnav{display:flex;gap:0.3rem;padding:0.4rem 0.6rem;background:var(--bg1);border-bottom:1px solid var(--bg3);position:sticky;top:calc(106px + var(--sat));z-index:49;overflow-x:auto;scrollbar-width:none;}
/* On mobile the dash-tabs row shrinks (48px min-height + 2px border =
   50px) so its bottom edge lands at scroll-y 102. Previous fix used
   top:100 which left a 2px overlap (subnav cutting into the tab
   bar's bottom edge). 102 is the correct flush offset. */
@media (max-width: 600px) { .dash-subnav { top: calc(102px + var(--sat)); } }
.dash-subnav::-webkit-scrollbar{display:none;}
/* Subtab pills: dark-scarlet background ringed in gold. Heraldic
   palette — dark enough to read on the page background, gold border
   gives the pill its "circled" look. Active state lights both the
   scarlet fill and the gold ring; inactive sits at lower intensity
   so the active pill always wins the eye. */
.dash-subtab{flex:0 0 auto;background:rgba(184,150,12,0.22);color:var(--text);border:1.5px solid rgba(255,215,0,0.45);border-radius:999px;padding:0.3rem 0.85rem;font-size:0.72rem;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:0.3rem;font-family:inherit;-webkit-tap-highlight-color:transparent;transition:background 0.12s,color 0.12s,border-color 0.12s,box-shadow 0.12s;letter-spacing:0.02em;}
.dash-subtab i{font-size:0.8rem;color:var(--gold);}
.dash-subtab:hover:not(.active){background:rgba(184,150,12,0.35);border-color:var(--gold);color:#fff;box-shadow:0 0 0 1px rgba(255,215,0,0.25) inset;}
.dash-subtab.active{background:linear-gradient(135deg, rgba(255,215,0,0.35), rgba(184,150,12,0.45));color:#1a0a00;border-color:var(--gold);box-shadow:0 0 0 1px rgba(255,215,0,0.4) inset, 0 0 8px -2px rgba(255,215,0,0.4);}
.dash-subtab.active i{color:#1a0a00;}

/* Hero sub-tabs — internal to the Hero panel (Stats / Skills / Features).
   Reuses .dash-subtab styling but lives in its own sticky strip. The
   active sub-panel is toggled via a class on each .hero-subpanel; the
   Features tab has two siblings (split by the combat & conditions
   modals) so we toggle by attribute match, not a parent selector. */
/* Solid bg0 so the subnav pills don't bleed page content when
   pinned. Sticky top:106 = topbar (52) + dash-tabs (52 min-height
   + 2 border). Sits flush with the tab bar bottom — no gap, no
   overlap. Previously at 108 with a 2px buffer, which read as a
   visible slit of page-bg between the two rows on scroll. */
.hero-subnav{display:flex;gap:0.3rem;padding:0.4rem 0.6rem;margin-bottom:0.65rem;overflow-x:auto;scrollbar-width:none;position:sticky;top:calc(106px + var(--sat));z-index:48;background:var(--bg1);border-bottom:1px solid var(--bg3);}
.hero-subnav::-webkit-scrollbar{display:none;}
.hero-subpanel{display:none;}
.hero-subpanel.active{display:block;}
@media (max-width: 600px) { .hero-subnav { top: calc(102px + var(--sat)); } }

/* ── Hero → "Sheet" page layout (2026-07-19) ───────────────────
   Layout adapted from the reference design in the abandoned parallel
   rebuild (redesign_prototypes/app/assets/shell.css) — same structural
   ideas (sticky vitals bar, identity rail, paired sections, in-page
   jump nav) but OUR existing dark/cool palette and Cinzel/Public Sans,
   NOT the reference's warm-brown palette or EB Garamond/JetBrains Mono
   (warm/brown was explicitly rejected earlier this session). Every
   selector below is scoped to #ppanel-hero specifically — .hero-subpanel
   and .hero-subnav are shared class names also used by Work/Workbench/
   Guild's own, unrelated sub-tab systems and must not be affected. */

/* All Hero sub-panels always visible (one scrolling page, not
   tab-switched) — scoped so Work/Workbench/Guild's own use of the
   same .hero-subpanel class elsewhere is untouched. */
#ppanel-hero .hero-subpanel { display: block; }

/* Stacked into explicit rows 2026-07-19 (was one wrapping flex row —
   identity/HP/buttons all mixed together, wrapped unpredictably). Now:
   row 1 = identity (name/class/level/badges), row 2 = HP + conditions +
   combat stats, row 3 = quick-access buttons.

   Sticky `top` values (2026-07-20 fix): originally estimated assuming a
   .hero-subnav bar still sat between .dash-tabs and this banner (106px/
   102px/92px worth of height at the three breakpoints below — see
   .hero-subnav's own top values). That subnav was removed from the Hero
   page during the Sheet rebuild but these offsets were never adjusted,
   leaving a visible empty gap the height of the now-absent subnav
   between .dash-tabs and the vitals bar. Reduced by that same ~42px at
   each breakpoint so the banner sticks directly under the tabs. Padding/
   gap also tightened (0.6rem→0.45rem, 0.55rem→0.4rem) for a shorter
   overall banner. */
/* Own padding/gap fluid (clamp()) 2026-07-25, same pass as the identity/
   stat-tile content it wraps — roomier on desktop, more compact on
   phones, instead of one fixed size across every screen above 640px. */
.vitals-bar {
  position: sticky; top: calc(106px + var(--sat)); z-index: 47;
  display: flex; flex-direction: column; gap: clamp(0.3rem, 0.25rem + 0.3vw, 0.5rem);
  padding: clamp(0.35rem, 0.3rem + 0.3vw, 0.6rem) clamp(0.6rem, 0.5rem + 0.8vw, 1.1rem); margin: 0 -0.2rem 0.55rem;
  background: var(--bg1); border-bottom: 1px solid var(--bg3);
  box-shadow: 0 6px 16px -12px rgba(0,0,0,0.6);
}
.vitals-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

/* Identity/HP/XP rows as a 3-col grid (2026-07-20, revised same day):
   col A = name block width, col B = Inspiration button width, col C =
   flexible remainder for stats. HP/XP explicitly span col A + col B
   (grid-column: 1 / 3) so they extend to the right up through the
   Inspiration button but stop before the stat-row — not full banner
   width. Row 1's col C content (stat-row + Conditions button) is left
   unfilled on HP/XP's rows; sparse grid auto-placement just leaves
   those cells empty rather than needing an explicit placeholder div. */
.vitals-grid { display: grid; grid-template-columns: auto auto 1fr; gap: clamp(0.3rem, 0.25rem + 0.3vw, 0.5rem) clamp(0.35rem, 0.3rem + 0.5vw, 0.7rem); align-items: start; }
.vitals-grid .vitals-col-b { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.vitals-hpxp { grid-column: 1 / 3; }
@media (max-width: 640px) { .vitals-grid { grid-template-columns: 1fr; } }
/* .vitals-hpxp explicitly spans "column 1 to 3" (col A + col B) to match
   Inspiration's right edge on desktop's 3-column grid — but .vitals-grid
   collapses to a SINGLE explicit column below 640px, so that same span
   request asks for a grid line (3) that no longer exists. Grid satisfies
   it by implicitly generating an extra column track, which pulls the
   name/Inspiration/stat-row auto-placed items into a totally different
   2-up arrangement instead of each stacking on its own row as intended
   — that's what pushed Inspiration out of view and blew HP/XP's own
   width out past the screen (their fill bars are width:X%, so a wider-
   than-intended container stretches them along with it). Just span the
   single real column here instead of asking for a 2nd one that isn't
   there. */
@media (max-width: 640px) { .vitals-hpxp { grid-column: 1; } }
/* Below 640px the 7 combat-stat tiles used to flex-wrap based on their
   own content width, which meant every tile landed on its own full-
   width row (each needs more than half the row to fit its label/value
   text) — a lot of scrolling before reaching the actual character sheet.
   A 2-column grid (2026-07-26 fix) halved that height but stacked the
   tiles into 4 rows of pairs. A single scrollable row (2026-07-27
   follow-up) kept them on one line like desktop, but hid Save DC (the
   7th tile) behind a horizontal swipe most players never think to try.
   Settled on a 4-column grid instead (2026-07-27, same day) — 2 rows
   (4 tiles + 3), no scrolling needed, every stat visible at a glance. */
@media (max-width: 640px) {
  /* min-width:0 is load-bearing: #p-combat-stats is a grid item in
     .vitals-grid's single collapsed column, and grid items default to
     min-width:auto — without this override the track sizes itself to
     fit the row's full content width instead of the available viewport
     width, blowing out the whole grid (and every other row sharing it
     — HP/XP/Inspiration included) past the screen edge. */
  #p-combat-stats.stat-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.4rem; min-width: 0; }
  #p-combat-stats .stat-box { min-width: 0; }
}
/* .vitals-bar is sticky so a short reference strip (AC/Initiative/HP)
   stays visible while scrolling — reasonable on desktop, where the 7
   stat tiles fit on one line and the whole bar is compact. Below 640px
   .vitals-grid collapses to 1 column (name, Inspiration, stat tiles,
   conditions, all 6 ability scores stack vertically instead of sitting
   side by side), so .vitals-bar's own content height balloons past the
   viewport height. A sticky element taller than the viewport can't
   "stick and release" normally — found via mobile-viewport testing
   2026-07-24 that it just pins in place for a very long scroll distance
   with nothing visibly moving before it finally lets go, reading as a
   frozen/broken page rather than a helpful sticky header. Dropping
   sticky here (falls back to normal in-flow positioning) so mobile
   scrolling stays responsive; desktop keeps the original sticky
   behavior since it isn't tall enough to hit this problem. */
@media (max-width: 640px) { .vitals-bar { position: static; } }
@media (max-width: 600px) { .vitals-bar { top: calc(102px + var(--sat)); } }
@media (max-width: 480px) { .vitals-bar { top: calc(92px + var(--sat)); } }
.vital { display: flex; align-items: baseline; gap: 0.35rem; font-size: 0.82rem; }
.vital .v-label { color: var(--text2); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; }
.vital .v-value { font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }

.sheet-layout { display: grid; grid-template-columns: 190px 1fr; gap: 1.2rem; align-items: start; }
@media (max-width: 760px) { .sheet-layout { display: block; } }

/* Sticky offsets here are estimated from the topbar+dash-tabs+hero-subnav
   +vitals-bar stack height — may want a small visual tweak once reviewed
   in a real browser, easy to adjust, not load-bearing for anything else. */
.identity-rail { text-align: center; position: sticky; top: 200px; }
@media (max-width: 760px) { .identity-rail { position: static; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.8rem; text-align: left; } }
.identity-portrait {
  width: 84px; height: 84px; border-radius: 50%; background: var(--bg3);
  display: flex; align-items: center; justify-content: center; font-size: 2rem;
  margin: 0 auto 0.5rem; border: 2px solid var(--gold);
}
@media (max-width: 760px) { .identity-portrait { margin: 0; } }
.identity-name { font-family: var(--font-display); color: var(--gold); font-size: 1.05rem; font-weight: 700; }
.identity-sub { color: var(--text2); font-size: 0.76rem; margin-top: 0.15rem; }
.level-ring {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 50%; border: 2px solid var(--gold);
  font-weight: 700; color: var(--gold); font-size: 0.85rem; margin-top: 0.5rem;
}

.sheet-pair-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; align-items: start; }
@media (max-width: 900px) { .sheet-pair-grid { display: block; } }

.section-block { margin-bottom: 1.6rem; scroll-margin-top: 13rem; }
.section-block h2 {
  font-family: var(--font-display); font-size: 1.05rem; color: var(--gold);
  margin-bottom: 0.6rem; border-bottom: 1px solid rgba(255,215,0,0.25); padding-bottom: 0.35rem;
}

.with-section-nav { display: grid; grid-template-columns: 1fr 160px; gap: 1.2rem; align-items: start; }
/* Sticky top raised 200px → 350px (2026-07-20) — was set before this
   session's vitals-bar work (grid columns, HP/XP flare, tightened
   padding, extra stat tiles) changed its rendered height several times
   over; 200px landed inside the vitals-bar's own stuck region, so this
   panel was sticking too early and visually fighting the vitals-bar
   for the same screen space instead of cleanly appearing below it.
   350px is a reasoned estimate (vitals-bar's own top:106px + its
   current content height) — same "estimated, may want a small tweak
   once reviewed in a real browser" caveat as the vitals-bar's own
   offsets carried. Only applies ≥901px; below that this nav switches
   to position:static (a horizontal bar) via the media query below, so
   the narrower breakpoints where .vitals-grid stacks taller never hit
   this sticky math at all. */
.section-nav { position: sticky; top: 350px; display: flex; flex-direction: column; gap: 0.3rem; }
.section-nav a {
  font-size: 0.72rem; color: var(--text2); text-decoration: none;
  padding: 0.25rem 0.5rem; border-left: 2px solid transparent;
}
.section-nav a:hover, .section-nav a.active { color: var(--gold); border-left-color: var(--gold); }
@media (max-width: 900px) {
  .with-section-nav { display: block; }
  .section-nav {
    position: static; flex-direction: row; overflow-x: auto; -webkit-overflow-scrolling: touch;
    margin-bottom: 0.8rem; scrollbar-width: none; gap: 0.5rem;
  }
  .section-nav::-webkit-scrollbar { display: none; }
  .section-nav a { border-left: none; border-bottom: 2px solid transparent; white-space: nowrap; flex-shrink: 0; }
  .section-nav a:hover, .section-nav a.active { border-bottom-color: var(--gold); border-left-color: transparent; }
}

/* ── Floating Hot Button (2026-07-20) ─────────────────────────
   Global draggable quick-action launcher. Anchored via right/bottom
   (not left/top) so a JS-only clamp on load/resize is enough to keep
   it on-screen after a viewport change — see initHotButton() /
   _hotbtnLoadPos() in rw-core.js. */
#rw-hotbtn {
  position: fixed; right: 20px; bottom: 20px; z-index: 9400;
  width: 54px; height: 54px; border-radius: 50%;
  background: var(--bg1); border: 2px solid var(--gold);
  box-shadow: 0 4px 14px rgba(0,0,0,0.45);
  display: none; align-items: center; justify-content: center;
  color: var(--gold); font-size: 1.25rem;
  cursor: grab; touch-action: none; user-select: none;
  transition: box-shadow 0.15s, transform 0.15s;
}
#rw-hotbtn.visible { display: flex; }
#rw-hotbtn.dragging { cursor: grabbing; box-shadow: 0 8px 26px rgba(0,0,0,0.6); transform: scale(1.06); transition: none; }
#rw-hotbtn-menu { position: fixed; z-index: 9399; display: none; flex-direction: column; gap: 0.4rem; align-items: flex-end; }
#rw-hotbtn-menu.open { display: flex; }
.rw-hotbtn-item {
  display: flex; align-items: center; gap: 0.5rem;
  background: var(--bg1); border: 1px solid var(--bg3); border-radius: 999px;
  padding: 0.35rem 0.9rem 0.35rem 0.35rem;
  box-shadow: 0 2px 10px rgba(0,0,0,0.35);
  cursor: pointer; font-size: 0.76rem; font-weight: 700; color: var(--text);
  white-space: nowrap;
}
.rw-hotbtn-item:hover { border-color: var(--gold); }
.rw-hotbtn-item .rw-hotbtn-item-icon {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg2);
}
.rw-hotbtn-item.rw-hotbtn-customize { border-color: rgba(255,215,0,0.4); color: var(--gold); }
@media (max-width: 480px) {
  #rw-hotbtn { width: 46px; height: 46px; font-size: 1.05rem; }
}

/* ── DM player file: manila folder skin ──────────────────────
   Wraps the per-player file body so it reads as a physical
   case folder pulled out of a drawer. The active player sub-tab
   above visually attaches to the body via shared border tint
   (var(--folder-edge)). The .card descendants get a paper
   treatment — cream background, dark typewriter-feel text — to
   reinforce the "documents inside the folder" mental model. */
.dm-player-folder {
  --folder-paper: #f1e4c1;
  --folder-page:  #fcf5e3;
  --folder-edge:  #a07a3c;
  --folder-ink:   #2e1f0a;
  --folder-ink2:  #5b4423;
  background:
    repeating-linear-gradient(0deg, transparent 0 23px, rgba(101,67,33,0.04) 23px 24px),
    radial-gradient(ellipse at 50% 0%, rgba(255,255,255,0.05), transparent 60%),
    linear-gradient(135deg, var(--folder-paper) 0%, #ead8a4 100%);
  border: 1px solid var(--folder-edge);
  border-top-left-radius: 0;       /* corner where the active tab attaches */
  border-top-right-radius: 10px;
  border-bottom-left-radius: 10px;
  border-bottom-right-radius: 10px;
  box-shadow:
    inset 0 0 40px rgba(101,67,33,0.07),
    inset 0 -2px 0 rgba(101,67,33,0.08),
    0 6px 14px rgba(0,0,0,0.35),
    0 2px 4px rgba(0,0,0,0.2);
  padding: 1.5rem 1.4rem 2.2rem;
  position: relative;
  color: var(--folder-ink);
  animation: folderFlipIn 0.32s cubic-bezier(0.22, 0.61, 0.36, 1);
  transform-origin: left center;
}
/* Page-corner fold on the bottom-right — small triangle suggesting
   the folder isn't perfectly flat. Pure CSS, no asset. */
.dm-player-folder::after {
  content: '';
  position: absolute;
  right: 0; bottom: 0;
  width: 28px; height: 28px;
  background:
    linear-gradient(225deg, transparent 50%, rgba(101,67,33,0.18) 50%, var(--folder-paper) 50%);
  border-bottom-right-radius: 10px;
  box-shadow: -1px -1px 3px rgba(0,0,0,0.12);
  pointer-events: none;
}
/* Three faint binder-hole punches down the left edge — purely
   decorative, hidden on narrow screens to keep content room. */
.dm-player-folder::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 6px;
  width: 7px; height: 7px;
  margin-top: -3.5px;
  background: rgba(46,31,10,0.45);
  border-radius: 50%;
  box-shadow:
    0 -42px 0 rgba(46,31,10,0.45),
    0  42px 0 rgba(46,31,10,0.45);
  pointer-events: none;
}
@media (max-width: 600px) { .dm-player-folder::before { display: none; } }

/* Cards inside the folder become inserted papers. Override the
   dark theme card so they read as cream documents on tan. */
.dm-player-folder .card {
  background: var(--folder-page);
  background-image:
    repeating-linear-gradient(180deg, transparent 0 22px, rgba(101,67,33,0.05) 22px 23px);
  border: 1px solid rgba(101,67,33,0.22);
  color: var(--folder-ink);
  box-shadow:
    0 1px 0 rgba(255,255,255,0.5) inset,
    0 1px 2px rgba(0,0,0,0.12),
    0 4px 8px rgba(0,0,0,0.08);
}
.dm-player-folder .card h3,
.dm-player-folder .card strong { color: var(--folder-ink); }
.dm-player-folder .card .empty-state,
.dm-player-folder .card [style*="color:var(--text2)"] { color: var(--folder-ink2) !important; }
.dm-player-folder .card [style*="color:var(--text3)"] { color: rgba(91,68,35,0.65) !important; }
.dm-player-folder .card [style*="color:var(--text)"]:not([style*="color:var(--text2)"]):not([style*="color:var(--text3)"]) {
  color: var(--folder-ink) !important;
}
/* Tone the green/red money colors for parchment so they don't glow.
   We re-target them to muted forest green / oxblood. */
.dm-player-folder .card [style*="color:var(--green)"] { color: #2d6a3a !important; }
.dm-player-folder .card [style*="color:var(--red)"]   { color: #8b1a1a !important; }
.dm-player-folder .card [style*="color:var(--gold)"]  { color: #8a6a1c !important; }
.dm-player-folder .card [style*="color:var(--purple)"]{ color: #5a2d7a !important; }
.dm-player-folder .card [style*="color:var(--blue)"]  { color: #2d4d7a !important; }
/* Inline stat/finance/inventory tiles use var(--bg2)/var(--bg3) —
   re-skin those to a parchment-on-parchment scheme so they don't
   stay dark inside the folder. */
.dm-player-folder .card [style*="background:var(--bg2)"] {
  background: rgba(255,255,255,0.45) !important;
  border-color: rgba(101,67,33,0.18) !important;
}
.dm-player-folder .card [style*="border-bottom:1px dotted var(--bg3)"] {
  border-bottom-color: rgba(101,67,33,0.2) !important;
}
/* Header card (gradient-gold) inside the folder: keep the gold
   gradient, but tone it for the cream backdrop. */
.dm-player-folder > .card:first-child {
  background:
    repeating-linear-gradient(180deg, transparent 0 22px, rgba(101,67,33,0.05) 22px 23px),
    linear-gradient(135deg, rgba(255,215,0,0.18), rgba(252,245,227,1));
}
/* Player sub-tabs look like the protruding tabs of manila folders
   in a drawer. Override the .dash-subtab pill styling — sharp
   bottom corners, rounded tops, paper colour, ink lettering. The
   inactive tabs sit slightly recessed (darker tan) and the active
   tab pops forward in folder-paper colour to feel like the folder
   has been pulled out. The sub-nav above still reads as the
   "drawer" since the dashboard backdrop stays dark. */
#dmplayers-subnav {
  align-items: flex-end;        /* tabs hang from the same baseline */
  padding: 0.5rem 0.6rem 0;
  background: linear-gradient(180deg, var(--bg1) 0%, var(--bg2) 100%);
  border-bottom: 2px solid var(--folder-edge, #a07a3c);
  box-shadow: inset 0 8px 12px -8px rgba(0,0,0,0.5);
}
.dash-subtab.dm-player-folder-tab {
  font-family: Georgia, 'Times New Roman', serif;
  letter-spacing: 0.06em;
  background: linear-gradient(180deg, #c69e54 0%, #a07a3c 100%);
  color: rgba(46,31,10,0.78);
  border: 1px solid #7a5a25;
  border-bottom: none;
  border-top-left-radius: 14px;
  border-top-right-radius: 14px;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  padding: 0.45rem 0.9rem 0.55rem;
  margin-bottom: -2px;            /* overlap the drawer's bottom edge so it reads as continuous */
  box-shadow:
    inset 0 -2px 0 rgba(101,67,33,0.4),
    0 -2px 4px rgba(0,0,0,0.2);
  position: relative;
  z-index: 1;
  transition: transform 0.12s ease, background 0.12s ease;
}
.dash-subtab.dm-player-folder-tab:hover:not(.active) {
  background: linear-gradient(180deg, #d8b15e 0%, #b58a48 100%);
  color: var(--folder-ink, #2e1f0a);
  transform: translateY(-1px);
}
.dash-subtab.dm-player-folder-tab.active {
  background: linear-gradient(180deg, #f1e4c1 0%, #ead8a4 100%);
  color: var(--folder-ink, #2e1f0a);
  border-color: var(--folder-edge, #a07a3c);
  /* Active tab sits on top and extends below the drawer's edge so
     the eye reads it as one piece with the folder body. */
  z-index: 3;
  transform: translateY(-2px);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.5),
    0 -3px 6px rgba(0,0,0,0.15),
    -2px 0 4px rgba(0,0,0,0.05),
     2px 0 4px rgba(0,0,0,0.05);
}
.dash-subtab.dm-player-folder-tab i { color: rgba(46,31,10,0.65); }
.dash-subtab.dm-player-folder-tab.active i { color: var(--folder-ink, #2e1f0a); }
/* Status-dot sits on top of the icon — nudge to the upper-right
   like a coloured tab sticker, not a notification badge. */
.dash-subtab.dm-player-folder-tab i > span {
  top: -3px !important;
  right: -5px !important;
  bottom: auto !important;
  border-color: rgba(101,67,33,0.6) !important;
}

@keyframes folderFlipIn {
  0%   { opacity: 0; transform: translateX(-14px) rotateY(-6deg); filter: blur(1px); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateX(0) rotateY(0);        filter: blur(0); }
}
@media (prefers-reduced-motion: reduce) {
  .dm-player-folder { animation: none; }
}

/* Hero Quick Actions — on narrow phones, each row (Actions / Rest)
   stretches its buttons to 50/50 so Short Rest + Long Rest stack
   symmetrically on their own line instead of wrapping mid-row
   behind the Combat / Conditions pair. */
@media (max-width: 600px) {
  .hero-qa-group .hero-qa-btn { flex: 1 1 calc(50% - 0.2rem); min-height: 40px; justify-content: center; }
  .hero-qa-group > span { flex: 1 1 100%; margin-bottom: 0.25rem; }
}

/* ── Modal-as-inline-subpanel (Inventory / Spellbook / Edit Profile) ──
   Each of these Hero sub-tabs is backed by an existing modal whose
   markup we can't cheaply duplicate inline. Instead, when the
   sub-tab activates we portal-move the modal DOM into its hero
   host, then tag it with `.in-hero-sub` so the CSS below strips the
   overlay positioning and renders it as a normal block inside the
   sub-panel. Moving back on exit restores normal modal behavior for
   any non-Hero caller (e.g. the starting-gear wizard's "Skip"). */
.modal-backdrop.in-hero-sub {
  display: block !important;
  position: static !important;
  inset: auto !important;
  background: transparent !important;
  padding: 0 !important;
  z-index: auto !important;
  align-items: stretch !important;
  justify-content: stretch !important;
}
.modal-backdrop.in-hero-sub > .modal {
  max-width: 100% !important;
  max-height: none !important;
  width: 100% !important;
  box-shadow: none !important;
  border-radius: 8px !important;
  margin: 0 !important;
  /* Flex-column + overflow:hidden work for overlay mode where the
     modal is pinned to 92vh and the inner tab panels scroll. Inline,
     we want the whole thing to flow naturally so the page's normal
     scroll reaches the bottom — otherwise the last row of Backpack
     / Amazon / Prepared Spells gets clipped at the bottom edge of
     the flex container with nowhere to scroll to. */
  display: block !important;
  overflow: visible !important;
  height: auto !important;
}
/* Tab panels inside inventory + spellbook modals ride flex:1 with
   their own inner overflow-y:auto for overlay mode. Inline, force
   them to behave like regular block children so content flows with
   the page scroll. */
.modal-backdrop.in-hero-sub #invtab-mine,
.modal-backdrop.in-hero-sub #invtab-browse,
.modal-backdrop.in-hero-sub #invtab-amazon,
.modal-backdrop.in-hero-sub #invtab-components,
.modal-backdrop.in-hero-sub #invtab-log,
.modal-backdrop.in-hero-sub #invtab-stash,
.modal-backdrop.in-hero-sub #sbtab-known,
.modal-backdrop.in-hero-sub #sbtab-browse,
.modal-backdrop.in-hero-sub #sbtab-prepared,
.modal-backdrop.in-hero-sub #inv-section-equipped,
.modal-backdrop.in-hero-sub #inv-section-equipped > div,
.modal-backdrop.in-hero-sub #inv-section-stash,
.modal-backdrop.in-hero-sub #inv-section-stash > div {
  flex: none !important;
  overflow: visible !important;
  max-height: none !important;
  min-height: 0 !important;
  height: auto !important;
}
/* comp-list is a nested scroll container inside invtab-components; it must
   also lose its own overflow context in inline mode or mobile touch gestures
   get captured by it instead of the page scroll. */
.modal-backdrop.in-hero-sub #comp-list {
  overflow: visible !important;
  height: auto !important;
  flex: none !important;
  max-height: none !important;
}
/* Sticky header rows inside the modals (close button, tab bar) lose
   their sticky anchoring inline — the page scroll makes "sticky to
   viewport" more useful than "sticky to a now-unused flex container". */
.modal-backdrop.in-hero-sub > .modal > div[style*="flex-shrink:0"] {
  position: static !important;
}
/* Components shop search bar loses its sticky anchor in inline mode too. */
.modal-backdrop.in-hero-sub #invtab-components > div[style*="sticky"] {
  position: static !important;
}

/* Wealth sub-tabs — same mechanism as Hero. Summary is split into
   three sibling sub-panels because Familiars and Dashercoins cards
   sit between Summary content; switchWealthSub() matches by attr. */
.wealth-subpanel{display:none;}
.wealth-subpanel.active{display:block;}

/* ══ Gambling Den (2026-07-20) ═══════════════════════════════════════
   Ported from two standalone prototypes ("The Gilded Mimic" +
   "The Undercroft") into the Gambling sub-tab. Deliberately kept its
   own distinct purple/parchment "dungeon casino" look rather than
   reskinning to RealmWatch's palette — reads as a different place,
   same treatment the standalone minigame iframes already get.
   EVERY selector below is scoped under #gambling-den specifically
   because the source files used bare global class names (.card, .btn,
   .panel, .grid, .cell, .overlay...) that collide head-on with
   RealmWatch's own — an unscoped copy-paste would have repainted
   every card/button across the entire app purple. Two exceptions,
   called out inline: elements the JS appends to document.body
   directly (coin-burst particles, floating +/- numbers) got distinct
   .gd-* class names instead of scoping, since a descendant selector
   can't match an element that isn't actually inside #gambling-den in
   the DOM; and the "shake" effect targets the game overlay instead of
   the real document.body (shaking the whole RealmWatch page on a
   loss would be a jarring, undignified surprise for a KO'd browser). */
#gambling-den{
  --gd-ink:#120d14; --gd-char:#1a1420; --gd-parch:#241a2b; --gd-parch2:#2d2136;
  --gd-line:#4a3556; --gd-bone:#e8dcc3; --gd-dim:#a893b8;
  --gd-ember:#e0762e; --gd-gold:#d4a94e; --gd-moss:#7a9e6b; --gd-blood:#c14b32;
  --gd-arcane:#8a6fd1;
  font-family:'EB Garamond',serif; font-size:17px; color:var(--gd-bone);
  background:
    radial-gradient(900px 420px at 50% -10%, rgba(138,111,209,.12), transparent 60%),
    radial-gradient(700px 400px at 90% 110%, rgba(212,169,78,.06), transparent 60%),
    repeating-linear-gradient(0deg, rgba(255,255,255,.012) 0 2px, transparent 2px 4px),
    var(--gd-ink);
  border-radius: 10px; overflow: hidden; position: relative;
}
#gambling-den *{box-sizing:border-box;-webkit-tap-highlight-color:transparent;}
#gambling-den h1,#gambling-den h2,#gambling-den h3{font-family:'Cinzel',serif;margin:0;}
#gambling-den .mono{font-family:'JetBrains Mono',monospace;}
#gambling-den button{font-family:inherit;cursor:pointer;}
#gambling-den .wrap{max-width:900px;margin:0 auto;padding:18px 14px 30px;}
#gambling-den header.masthead{text-align:center;padding:22px 8px 10px;}
#gambling-den .masthead .eyebrow{font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.28em;
  text-transform:uppercase;color:var(--gd-arcane);}
#gambling-den .masthead h1{font-size:clamp(26px,6vw,44px);font-weight:900;letter-spacing:.06em;
  color:var(--gd-gold);text-shadow:0 2px 14px rgba(212,169,78,.3);
  background:linear-gradient(100deg,var(--gd-gold) 40%,#fff3cf 50%,var(--gd-gold) 60%);background-size:200% 100%;
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;animation:gdShimmer 4s linear infinite;}
#gambling-den .masthead .sub{color:var(--gd-dim);font-style:italic;margin-top:6px;font-size:15px;}
#gambling-den .ledger{display:flex;justify-content:center;gap:14px;margin:16px 0 6px;flex-wrap:wrap;
  font-family:'JetBrains Mono',monospace;font-size:13px;}
#gambling-den .ledger .stat{background:var(--gd-char);border:1px solid var(--gd-line);border-radius:8px;
  padding:8px 14px;display:flex;align-items:center;gap:8px;}
#gambling-den .ledger .val{color:var(--gd-gold);font-weight:700;}
#gambling-den .coin{display:inline-block;width:14px;height:14px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%, #f5d98a, #d4a94e 55%, #8a6a24);
  box-shadow:0 0 6px rgba(212,169,78,.6);}
#gambling-den .board{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:12px;margin-top:14px;}
#gambling-den .gd-card{position:relative;background:linear-gradient(160deg,var(--gd-parch2),var(--gd-parch));
  border:1px solid var(--gd-line);border-radius:10px;padding:16px 12px 12px;
  display:flex;flex-direction:column;align-items:center;gap:8px;
  transition:transform .12s, border-color .12s;user-select:none;cursor:pointer;
  animation:gdMarquee 3.2s ease-in-out infinite;}
#gambling-den .gd-card:nth-child(2n){animation-delay:.8s;} #gambling-den .gd-card:nth-child(3n){animation-delay:1.6s;}
#gambling-den .gd-card:active{transform:scale(.97);}
#gambling-den .gd-card:hover{border-color:var(--gd-gold);}
#gambling-den .gd-card .sigil{width:52px;height:52px;animation:gdSigilbob 2.6s ease-in-out infinite;}
#gambling-den .gd-card .sigil svg{width:100%;height:100%;stroke:var(--gd-gold);fill:none;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;filter:drop-shadow(0 0 4px rgba(212,169,78,.35));}
#gambling-den .gd-card h3{font-size:15px;font-weight:700;letter-spacing:.04em;text-align:center;color:var(--gd-bone);}
#gambling-den .gd-card .desc{font-size:12.5px;color:var(--gd-dim);font-style:italic;text-align:center;line-height:1.3;}
#gambling-den .gd-card .odds{font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--gd-arcane);letter-spacing:.08em;}
#gambling-den footer.colophon{text-align:center;margin-top:34px;color:var(--gd-dim);font-size:13px;font-style:italic;line-height:1.6;}
#gambling-den footer .mono{font-size:10.5px;letter-spacing:.2em;color:#6a5c7a;}
#gambling-den .overlay{position:fixed;inset:0;background:var(--gd-ink);z-index:9999;display:none;flex-direction:column;}
#gambling-den .overlay.open{display:flex;}
#gambling-den .gbar{display:flex;align-items:center;gap:10px;padding:10px 12px;
  border-bottom:1px solid var(--gd-line);background:var(--gd-char);}
#gambling-den .gbar .back{background:none;border:1px solid var(--gd-line);color:var(--gd-bone);
  border-radius:8px;padding:6px 12px;font-family:'Cinzel',serif;font-size:12px;letter-spacing:.06em;}
#gambling-den .gbar .gtitle{flex:1;min-width:0;}
#gambling-den .gbar .gtitle h2{font-size:15px;color:var(--gd-gold);letter-spacing:.05em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
#gambling-den .gbar .bank{font-family:'JetBrains Mono',monospace;font-size:14px;color:var(--gd-gold);font-weight:700;
  display:flex;align-items:center;gap:6px;white-space:nowrap;}
#gambling-den .stage{flex:1;position:relative;overflow-y:auto;scrollbar-gutter:stable;display:flex;flex-direction:column;}
#gambling-den .play{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:14px;padding:16px;width:100%;max-width:560px;margin:0 auto;}
#gambling-den .bigmsg{font-family:'Cinzel',serif;font-size:18px;color:var(--gd-gold);min-height:24px;text-align:center;}
#gambling-den .subhud{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--gd-dim);text-align:center;min-height:16px;}
#gambling-den .good{color:var(--gd-moss)!important;} #gambling-den .bad{color:var(--gd-blood)!important;}
#gambling-den .betstrip{display:flex;gap:8px;align-items:center;flex-wrap:wrap;justify-content:center;}
#gambling-den .gd-chip{width:52px;height:52px;border-radius:50%;font-family:'JetBrains Mono',monospace;font-weight:700;
  font-size:13px;color:#1a1420;border:3px dashed rgba(0,0,0,.35);
  background:radial-gradient(circle at 35% 30%, #f5d98a, #d4a94e 60%, #a97e22);
  box-shadow:0 3px 8px rgba(0,0,0,.5);}
#gambling-den .gd-chip:active{transform:scale(.92);}
#gambling-den .gd-chip.sel{outline:3px solid var(--gd-arcane);outline-offset:2px;}
#gambling-den .betline{font-family:'JetBrains Mono',monospace;font-size:14px;color:var(--gd-bone);}
#gambling-den .gd-btn{background:linear-gradient(180deg,#e98a3f,#c8611f);border:1px solid #f2a763;color:#fff8ea;
  border-radius:10px;padding:12px 30px;font-family:'Cinzel',serif;font-weight:700;
  font-size:15px;letter-spacing:.08em;box-shadow:0 3px 14px rgba(224,118,46,.35);}
#gambling-den .gd-btn:disabled{opacity:.4;box-shadow:none;}
#gambling-den .gd-btn:active:not(:disabled){transform:scale(.96);}
#gambling-den .gd-btn.ghost{background:none;border:1px solid var(--gd-line);color:var(--gd-bone);box-shadow:none;}
#gambling-den .gd-btn.arc{background:linear-gradient(180deg,#9a80df,#6a4fb1);border-color:#b9a5ec;box-shadow:0 3px 14px rgba(138,111,209,.35);}
#gambling-den .gd-tapbtn{background:linear-gradient(180deg,var(--gd-parch2),var(--gd-parch));border:1px solid var(--gd-line);
  color:var(--gd-bone);border-radius:14px;padding:14px 18px;font-family:'Cinzel',serif;font-weight:700;
  font-size:14px;letter-spacing:.05em;}
#gambling-den .gd-tapbtn:active{border-color:var(--gd-gold);transform:scale(.96);}
#gambling-den .gd-tapbtn.sel{border-color:var(--gd-gold);background:var(--gd-char);color:var(--gd-gold);}
#gambling-den .gd-grid{display:grid;gap:6px;}
#gambling-den .gd-cell{background:var(--gd-parch2);border:1px solid var(--gd-line);border-radius:8px;
  display:flex;align-items:center;justify-content:center;user-select:none;
  transition:background .12s, transform .08s;}
#gambling-den .gd-cell:active{transform:scale(.94);}
#gambling-den .win-flash{animation:gdWinf .6s ease;}
@keyframes gdWinf{0%{filter:brightness(2.2)}100%{filter:none}}
#gambling-den .gd-panel{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:14px;padding:26px;text-align:center;background:rgba(18,13,20,.95);z-index:5;}
#gambling-den .gd-panel h2{color:var(--gd-gold);font-size:22px;}
#gambling-den .gd-panel p{max-width:460px;line-height:1.5;margin:0;font-size:16px;}
#gambling-den .gd-panel .flavor{color:var(--gd-dim);font-style:italic;font-size:14px;}
@keyframes gdShakeX{0%,100%{transform:translate(0,0)}20%{transform:translate(-7px,2px)}40%{transform:translate(6px,-3px)}60%{transform:translate(-5px,3px)}80%{transform:translate(4px,-2px)}}
/* Shakes the game overlay, not the real page body (see file-header note). */
#gambling-den .overlay.gd-shake{animation:gdShakeX .4s ease;}
@keyframes gdDealIn{0%{transform:translateY(-26px) rotate(-8deg) scale(.6);opacity:0}60%{transform:translateY(3px) rotate(2deg) scale(1.05)}100%{transform:translateY(0) rotate(0) scale(1);opacity:1}}
#gambling-den .gd-dealcard{animation:gdDealIn .28s cubic-bezier(.2,1.4,.4,1) both;}
@keyframes gdShimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}
/* .gd-particle / .gd-floatnum are appended to document.body directly by
   JS (coin-burst + floating +/- numbers read from the bank element's
   live screen position) — NOT scoped under #gambling-den since a
   descendant selector can't match an element outside that subtree.
   Distinctively prefixed instead to avoid colliding with anything else
   appended to body. */
.gd-particle{position:fixed;z-index:10000;pointer-events:none;font-size:20px;will-change:transform,opacity;}
@keyframes gdPopUp{0%{transform:translate(-50%,0) scale(.7);opacity:0}20%{opacity:1;transform:translate(-50%,-10px) scale(1.15)}100%{transform:translate(-50%,-46px) scale(1);opacity:0}}
.gd-floatnum{position:fixed;z-index:10000;pointer-events:none;font-family:'JetBrains Mono',monospace;font-weight:700;
  font-size:17px;animation:gdPopUp 1s ease forwards;text-shadow:0 2px 6px #000;transform:translate(-50%,0);}
@keyframes gdPulseGold{0%,100%{transform:scale(1)}50%{transform:scale(1.12);text-shadow:0 0 18px rgba(212,169,78,.9)}}
#gambling-den .gd-bigpulse{animation:gdPulseGold .5s ease 2;}
@keyframes gdMarquee{0%,100%{box-shadow:0 0 6px rgba(212,169,78,.25)}50%{box-shadow:0 0 18px rgba(212,169,78,.55)}}
@keyframes gdSigilbob{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}
#gambling-den .mutebtn{background:none;border:1px solid var(--gd-line);color:var(--gd-dim);border-radius:8px;padding:6px 10px;font-size:14px;}
@media (prefers-reduced-motion: reduce){#gambling-den *{animation-duration:.01ms!important;transition-duration:.01ms!important;}}

/* Skullduggery door — click-to-investigate (2026-07-26). Lives outside
   #skullduggery-arcade (it's the normal in-page card players see before
   the overlay ever opens), so intentionally not scoped under that id. */
#sk-door-card svg#sk-door-illo{transition:filter .15s;}
#sk-door-card svg#sk-door-illo:hover{filter:drop-shadow(0 0 8px rgba(201,162,39,.35));}
@keyframes skDoorShake{0%,100%{transform:rotate(0) translateX(0);}20%{transform:rotate(-2deg) translateX(-2px);}40%{transform:rotate(2deg) translateX(2px);}60%{transform:rotate(-1.5deg) translateX(-1px);}80%{transform:rotate(1deg) translateX(1px);}}
#sk-door-card svg#sk-door-illo.sk-door-shake{animation:skDoorShake .35s ease-in-out;}
@media (prefers-reduced-motion: reduce){#sk-door-card svg#sk-door-illo.sk-door-shake{animation:none;}}

/* ══ Skullduggery Arcade (2026-07-20) ═══════════════════════════════
   Ported from a standalone prototype ("The Crimson Hand Hideout") into
   the door behind the Gambling tab. Same scoping discipline as
   #gambling-den above: every selector lives under #skullduggery-arcade
   and every class is sk-prefixed, even names that don't currently
   collide with anything, so this stays a self-contained, greppable
   unit inside a very large stylesheet. Its own distinct palette,
   separate from both RealmWatch's cool theme and the Gilded Mimic's
   purple — reads as yet another different place, same as the Den does
   relative to the main app.
   Unlike #gambling-den, nothing here gets appended to document.body
   directly (the ticket-stub animation appends into its own #sk-
   dispenser container, which is itself a normal descendant of
   #skullduggery-arcade) — so no unscoped exceptions are needed. The
   source's <header>/<main> landmark tags were converted to plain
   classed divs (.sk-header/.sk-main) since a page can only have one
   real <main>, and this whole thing lives inside RealmWatch's own. */
/* Full-screen overlay (2026-07-26) — was an inline card sharing scroll
   with the rest of the Gambling tab; now covers the whole viewport like
   #gambling-den .overlay/#dashing-overlay, so entering the arcade reads
   as its own place instead of just another section of the page. Visible
   state is still toggled the same way as before (arcade.style.display),
   just 'flex'/'none' now instead of ''/'none' since this needs to be a
   flex column (header + scrollable .sk-main) to fill the viewport. */
/* Retheme 2026-07-26 — was a warm amber/parchment "carnival midway"
   palette; shifted the whole base to cool near-black/shadow-slate so it
   reads as an actual back-alley thieves' den (moonlit, secretive) rather
   than a lit-up fairground tent. --sk-ember/--sk-ember-bright are the
   ONE deliberate exception, left as warm orange — Lockpick Alley's own
   rules text literally says "the ember marker," so recoloring it would
   make the game's copy and its visuals disagree. Reads now as: a dark,
   quiet room with a single lit ember, ill-gotten gold, and old blood —
   not a brightly-lit midway. */
#skullduggery-arcade{
  --sk-ink:#0a090d; --sk-parch-deep:#131019; --sk-parch:#1b1722; --sk-parch-raised:#241f2e;
  --sk-line:#3a3348; --sk-text:#d6d2de; --sk-text-dim:#8b84a0;
  --sk-ember:#c9541e; --sk-ember-bright:#e8702f; --sk-gold:#c9a227; --sk-gold-bright:#e8c65a;
  --sk-crimson:#7a1f2e; --sk-crimson-bright:#b83248; --sk-safe:#3f7a52; --sk-danger:#b83248; --sk-ticket:#c9a227;
  background:var(--sk-parch-deep);
  background-image:
    radial-gradient(ellipse 80% 60% at 50% -10%, rgba(201,84,30,.08), transparent 60%),
    radial-gradient(ellipse 60% 50% at 85% 110%, rgba(122,31,46,.10), transparent 60%);
  color:var(--sk-text); font-family:'EB Garamond',serif; font-size:17px; line-height:1.5;
  position:fixed; inset:0; z-index:9999; flex-direction:column; overflow:hidden; border-radius:0;
}
#skullduggery-arcade *{box-sizing:border-box;-webkit-tap-highlight-color:transparent;}
#skullduggery-arcade ::selection{background:var(--sk-ember);color:var(--sk-ink)}
#skullduggery-arcade .sk-header{border-bottom:1px solid var(--sk-line);background:linear-gradient(to bottom, rgba(46,36,25,.9), rgba(26,20,16,.95));flex-shrink:0;z-index:50;backdrop-filter:blur(4px);padding-top:var(--sat);}
#skullduggery-arcade .sk-hd-inner{max-width:1060px;margin:0 auto;padding:12px 20px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}
#skullduggery-arcade .sk-exit-btn{background:none;border:1px solid var(--sk-line);color:var(--sk-text);border-radius:3px;padding:7px 12px;font-family:'Cinzel',serif;font-weight:700;font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;cursor:pointer;flex-shrink:0;}
#skullduggery-arcade .sk-exit-btn:hover{border-color:var(--sk-gold);color:var(--sk-gold-bright);}
#skullduggery-arcade .sk-hd-mark{display:flex;align-items:center;gap:12px;cursor:pointer;background:none;border:none;color:inherit;font:inherit;text-align:left}
#skullduggery-arcade .sk-hd-mark:focus-visible{outline:2px solid var(--sk-gold);outline-offset:3px}
#skullduggery-arcade .sk-hd-hand{width:34px;height:34px;flex:none}
#skullduggery-arcade .sk-hd-title{font-family:'Cinzel',serif;font-weight:900;font-size:1.05rem;letter-spacing:.08em;color:var(--sk-gold-bright);line-height:1.1}
#skullduggery-arcade .sk-hd-sub{font-family:'JetBrains Mono',monospace;font-size:.62rem;letter-spacing:.14em;color:var(--sk-text-dim);text-transform:uppercase}
#skullduggery-arcade .sk-hd-spacer{flex:1}
#skullduggery-arcade .sk-ticket-chip{display:flex;align-items:center;gap:10px;border:1px solid var(--sk-gold);border-radius:3px;background:rgba(201,162,39,.08);padding:7px 14px;font-family:'JetBrains Mono',monospace;}
#skullduggery-arcade .sk-ticket-chip .sk-tc-num{font-size:1.05rem;font-weight:700;color:var(--sk-gold-bright);min-width:3ch;text-align:right}
#skullduggery-arcade .sk-ticket-chip .sk-tc-lbl{font-size:.6rem;letter-spacing:.16em;color:var(--sk-text-dim);text-transform:uppercase}
#skullduggery-arcade .sk-btn{font-family:'Cinzel',serif;font-weight:700;font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;color:var(--sk-text);background:var(--sk-parch-raised);border:1px solid var(--sk-line);border-radius:3px;padding:9px 18px;cursor:pointer;transition:border-color .15s, color .15s, background .15s;}
#skullduggery-arcade .sk-btn:hover{border-color:var(--sk-gold);color:var(--sk-gold-bright)}
#skullduggery-arcade .sk-btn:focus-visible{outline:2px solid var(--sk-gold);outline-offset:2px}
#skullduggery-arcade .sk-btn.primary{border-color:var(--sk-ember);color:var(--sk-ember-bright);background:rgba(201,84,30,.1)}
#skullduggery-arcade .sk-btn.primary:hover{background:rgba(201,84,30,.2);color:#ffb27a}
#skullduggery-arcade .sk-btn.quiet{border-color:transparent;color:var(--sk-text-dim)}
#skullduggery-arcade .sk-btn.quiet:hover{color:var(--sk-text);border-color:var(--sk-line)}
#skullduggery-arcade .sk-btn:disabled{opacity:.4;cursor:not-allowed}
#skullduggery-arcade .sk-main{max-width:1060px;margin:0 auto;padding:28px 20px 60px;flex:1;overflow-y:auto;scrollbar-gutter:stable;overscroll-behavior:contain;}

#skullduggery-arcade .sk-screen{display:none}
#skullduggery-arcade .sk-screen.active{display:block;animation:skScreenIn .25s ease}
@keyframes skScreenIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){#skullduggery-arcade .sk-screen.active{animation:none} #skullduggery-arcade *{transition:none!important;animation-duration:.01ms!important}}

#skullduggery-arcade .sk-lobby-hero{text-align:center;padding:18px 0 30px;border-bottom:1px solid var(--sk-line);margin-bottom:30px}
#skullduggery-arcade .sk-lobby-eyebrow{font-family:'JetBrains Mono',monospace;font-size:.66rem;letter-spacing:.22em;color:var(--sk-ember-bright);text-transform:uppercase}
#skullduggery-arcade .sk-lobby-hero h1{font-family:'Cinzel',serif;font-weight:900;font-size:clamp(1.9rem,5vw,3.1rem);letter-spacing:.05em;color:var(--sk-gold-bright);margin:8px 0 4px;text-shadow:0 0 34px rgba(201,162,39,.25);}
#skullduggery-arcade .sk-lobby-hero .sk-flavor{max-width:600px;margin:10px auto 0;color:var(--sk-text-dim);font-style:italic}
#skullduggery-arcade .sk-legal{font-family:'JetBrains Mono',monospace;font-size:.6rem;letter-spacing:.06em;color:#6b5c43;margin-top:14px}
#skullduggery-arcade .sk-cabinets{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px}
#skullduggery-arcade .sk-cab{border:1px solid var(--sk-line);border-radius:4px;background:linear-gradient(160deg, var(--sk-parch-raised), var(--sk-parch));padding:0;overflow:hidden;text-align:left;color:inherit;font:inherit;cursor:pointer;transition:transform .15s, border-color .15s, box-shadow .15s;display:flex;flex-direction:column;animation:skMarquee 3.2s ease-in-out infinite;}
#skullduggery-arcade .sk-cab:nth-child(2n){animation-delay:.8s;} #skullduggery-arcade .sk-cab:nth-child(3n){animation-delay:1.6s;}
#skullduggery-arcade .sk-cab:hover{transform:translateY(-3px);border-color:var(--sk-gold);box-shadow:0 10px 30px rgba(0,0,0,.4), 0 0 24px rgba(201,162,39,.08)}
#skullduggery-arcade .sk-cab:focus-visible{outline:2px solid var(--sk-gold);outline-offset:3px}
@keyframes skMarquee{0%,100%{box-shadow:0 0 6px rgba(201,162,39,.2)}50%{box-shadow:0 0 16px rgba(201,162,39,.45)}}
#skullduggery-arcade .sk-cab-marquee{background:linear-gradient(to bottom, rgba(142,43,43,.35), rgba(142,43,43,.12));border-bottom:1px solid var(--sk-line);padding:10px 16px;display:flex;align-items:center;gap:10px;}
#skullduggery-arcade .sk-cab-marquee svg{width:26px;height:26px;flex:none}
#skullduggery-arcade .sk-cab-name{font-family:'Cinzel',serif;font-weight:700;font-size:.98rem;letter-spacing:.06em;color:var(--sk-gold-bright)}
#skullduggery-arcade .sk-cab-body{padding:14px 16px 16px;flex:1;display:flex;flex-direction:column;gap:10px}
#skullduggery-arcade .sk-cab-desc{font-size:.95rem;color:var(--sk-text-dim)}
#skullduggery-arcade .sk-cab-meta{margin-top:auto;display:flex;justify-content:space-between;align-items:baseline;font-family:'JetBrains Mono',monospace;font-size:.64rem;letter-spacing:.1em;color:var(--sk-text-dim);text-transform:uppercase}
#skullduggery-arcade .sk-cab-meta .sk-hs{color:var(--sk-gold)}
#skullduggery-arcade .sk-counter-cab .sk-cab-marquee{background:linear-gradient(to bottom, rgba(201,162,39,.28), rgba(201,162,39,.08))}

#skullduggery-arcade .sk-game-head{display:flex;align-items:center;gap:14px;margin-bottom:6px;flex-wrap:wrap}
#skullduggery-arcade .sk-game-head h2{font-family:'Cinzel',serif;font-weight:900;font-size:1.5rem;letter-spacing:.06em;color:var(--sk-gold-bright)}
#skullduggery-arcade .sk-game-head .sk-rules{color:var(--sk-text-dim);font-style:italic;font-size:.95rem;width:100%}
#skullduggery-arcade .sk-stage{border:1px solid var(--sk-line);border-radius:4px;background:var(--sk-parch);margin-top:14px;padding:22px;min-height:380px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;position:relative;overflow:hidden;}
#skullduggery-arcade .sk-hud{display:flex;gap:22px;font-family:'JetBrains Mono',monospace;font-size:.78rem;letter-spacing:.08em;color:var(--sk-text-dim);text-transform:uppercase;flex-wrap:wrap;justify-content:center}
#skullduggery-arcade .sk-hud b{color:var(--sk-gold-bright);font-weight:700}
#skullduggery-arcade .sk-hud .sk-bad{color:var(--sk-crimson-bright)}
#skullduggery-arcade .sk-result{text-align:center;display:flex;flex-direction:column;gap:10px;align-items:center;}
#skullduggery-arcade .sk-result .sk-verdict{font-family:'Cinzel',serif;font-weight:700;font-size:1.3rem;letter-spacing:.08em;color:var(--sk-gold-bright)}
#skullduggery-arcade .sk-result .sk-verdict.busted{color:var(--sk-crimson-bright)}
#skullduggery-arcade .sk-result .sk-memo{color:var(--sk-text-dim);font-style:italic;max-width:440px}
#skullduggery-arcade .sk-payout{font-family:'JetBrains Mono',monospace;font-size:.85rem;color:var(--sk-ticket);letter-spacing:.1em}

#skullduggery-arcade .sk-lock-wrap{display:flex;gap:14px;align-items:flex-end}
#skullduggery-arcade .sk-pin-col{width:56px;display:flex;flex-direction:column;align-items:center;gap:8px}
#skullduggery-arcade .sk-pin-track{width:34px;height:220px;border:1px solid var(--sk-line);border-radius:3px;background:linear-gradient(to bottom,#161009,#211a11);position:relative;overflow:hidden;}
#skullduggery-arcade .sk-pin-zone{position:absolute;left:0;right:0;background:rgba(201,162,39,.28);border-top:1px solid var(--sk-gold);border-bottom:1px solid var(--sk-gold)}
#skullduggery-arcade .sk-pin-marker{position:absolute;left:2px;right:2px;height:5px;background:var(--sk-ember-bright);border-radius:2px;box-shadow:0 0 8px rgba(232,112,47,.8)}
#skullduggery-arcade .sk-pin-col.set .sk-pin-track{border-color:var(--sk-gold);background:rgba(201,162,39,.12)}
#skullduggery-arcade .sk-pin-col.set .sk-pin-marker{background:var(--sk-gold-bright);box-shadow:0 0 10px rgba(236,196,119,.9)}
#skullduggery-arcade .sk-pin-col.jammed .sk-pin-track{border-color:var(--sk-crimson)}
#skullduggery-arcade .sk-pin-lbl{font-family:'JetBrains Mono',monospace;font-size:.62rem;color:var(--sk-text-dim);letter-spacing:.1em}
#skullduggery-arcade .sk-pin-col.set .sk-pin-lbl{color:var(--sk-gold)}
#skullduggery-arcade .sk-bigpress{font-family:'Cinzel',serif;font-weight:700;letter-spacing:.12em;text-transform:uppercase;font-size:.9rem;padding:14px 44px;border-radius:3px;border:1px solid var(--sk-ember);cursor:pointer;color:var(--sk-ember-bright);background:rgba(201,84,30,.12);}
#skullduggery-arcade .sk-bigpress:hover{background:rgba(201,84,30,.25)}
#skullduggery-arcade .sk-bigpress:focus-visible{outline:2px solid var(--sk-gold);outline-offset:2px}

#skullduggery-arcade .sk-market{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;width:min(440px,100%)}
#skullduggery-arcade .sk-stall{aspect-ratio:1;border:1px solid var(--sk-line);border-radius:4px;background:linear-gradient(to bottom, rgba(46,36,25,.6) 0 28%, transparent 28%),var(--sk-parch-raised);display:flex;align-items:center;justify-content:center;cursor:pointer;position:relative;overflow:hidden;padding:0;}
#skullduggery-arcade .sk-stall:focus-visible{outline:2px solid var(--sk-gold);outline-offset:2px}
#skullduggery-arcade .sk-stall .sk-occ{width:62%;height:62%;transform:translateY(70%) scale(.6);opacity:0;transition:transform .12s ease-out, opacity .12s;pointer-events:none}
/* skUseSym() (rw-core.js) creates these <svg> wrappers without an
   explicit width/height/viewBox when called with no w/h args (Cutpurse
   Market's stall icons never pass any) — an unsized <svg> defaults to
   the browser's replaced-element size of 300x150px, which .sk-stall's
   overflow:hidden then clips down to nothing visible. Forcing it to
   fill .sk-occ here fixes it at the CSS layer so every skUseSym() call
   renders correctly regardless of whether it happens to pass explicit
   dimensions. */
#skullduggery-arcade .sk-occ svg{width:100%;height:100%;display:block;}
#skullduggery-arcade .sk-stall.up .sk-occ{transform:translateY(6%) scale(1);opacity:1}
#skullduggery-arcade .sk-stall.hit-good{background:rgba(201,162,39,.25)}
#skullduggery-arcade .sk-stall.hit-bad{background:rgba(184,50,72,.3)}

#skullduggery-arcade .sk-shell-table{position:relative;width:min(480px,92vw);height:220px}
#skullduggery-arcade .sk-shell{position:absolute;top:40px;width:26%;height:120px;cursor:pointer;border:none;background:none;padding:0;transition:left .38s cubic-bezier(.5,.1,.4,1), top .38s cubic-bezier(.5,.1,.4,1);}
#skullduggery-arcade .sk-shell:focus-visible{outline:2px solid var(--sk-gold);outline-offset:3px;border-radius:6px}
#skullduggery-arcade .sk-shell svg{width:100%;height:100%;filter:drop-shadow(0 6px 10px rgba(0,0,0,.5))}
#skullduggery-arcade .sk-shell.lifted{top:-6px}
#skullduggery-arcade .sk-shell-coin{position:absolute;width:44px;height:44px;top:128px;transform:translateX(-50%);transition:left .38s cubic-bezier(.5,.1,.4,1), opacity .2s;}
#skullduggery-arcade .sk-shells-disabled .sk-shell{pointer-events:none}

#skullduggery-arcade .sk-canvas{width:min(680px,100%);border:1px solid var(--sk-line);border-radius:4px;background:#0c0906;touch-action:none;cursor:pointer}
#skullduggery-arcade .sk-lantern-state{font-family:'Cinzel',serif;font-weight:700;letter-spacing:.14em;font-size:.85rem;text-transform:uppercase}
#skullduggery-arcade .sk-lantern-state.safe{color:var(--sk-safe)}
#skullduggery-arcade .sk-lantern-state.warn{color:var(--sk-gold-bright)}
#skullduggery-arcade .sk-lantern-state.watch{color:var(--sk-crimson-bright)}

#skullduggery-arcade .sk-seal-desk{display:grid;grid-template-columns:repeat(2,120px);gap:16px}
#skullduggery-arcade .sk-seal{width:120px;height:120px;border-radius:50%;cursor:pointer;padding:0;border:2px solid var(--sk-line);background:radial-gradient(circle at 38% 32%, #3a2a1c, #241a10 70%);display:flex;align-items:center;justify-content:center;transition:border-color .1s, box-shadow .1s, transform .1s;}
#skullduggery-arcade .sk-seal svg{width:64%;height:64%}
#skullduggery-arcade .sk-seal.lit{border-color:var(--sk-gold-bright);box-shadow:0 0 22px rgba(236,196,119,.5);transform:scale(1.05)}
#skullduggery-arcade .sk-seal.lit-bad{border-color:var(--sk-crimson-bright);box-shadow:0 0 22px rgba(184,50,72,.5)}
#skullduggery-arcade .sk-seal:focus-visible{outline:2px solid var(--sk-gold);outline-offset:3px}
#skullduggery-arcade .sk-seal:disabled{cursor:default;opacity:.85}

#skullduggery-arcade .sk-prizes{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:16px;margin-top:18px}
#skullduggery-arcade .sk-prize{border:1px solid var(--sk-line);border-radius:4px;background:var(--sk-parch-raised);padding:16px;display:flex;flex-direction:column;gap:8px;}
#skullduggery-arcade .sk-prize h3{font-family:'Cinzel',serif;font-weight:700;font-size:.95rem;letter-spacing:.05em;color:var(--sk-text)}
#skullduggery-arcade .sk-prize p{font-size:.9rem;color:var(--sk-text-dim);font-style:italic;flex:1}
#skullduggery-arcade .sk-prize .sk-cost{font-family:'JetBrains Mono',monospace;font-size:.72rem;letter-spacing:.1em;color:var(--sk-ticket)}
#skullduggery-arcade .sk-prize.owned{border-color:var(--sk-gold)}
#skullduggery-arcade .sk-prize.owned h3{color:var(--sk-gold-bright)}
#skullduggery-arcade .sk-owned-tag{font-family:'JetBrains Mono',monospace;font-size:.62rem;letter-spacing:.14em;color:var(--sk-gold);text-transform:uppercase}

#skullduggery-arcade #sk-dispenser{position:fixed;left:0;right:0;bottom:0;z-index:9998;pointer-events:none;height:0;display:flex;justify-content:center;}
#skullduggery-arcade .sk-stub{position:absolute;bottom:-70px;width:120px;height:56px;background:var(--sk-ticket);color:var(--sk-ink);border-radius:3px;font-family:'JetBrains Mono',monospace;font-size:.55rem;letter-spacing:.08em;text-transform:uppercase;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;box-shadow:0 4px 14px rgba(0,0,0,.5);
  -webkit-mask:radial-gradient(circle 5px at 0 50%, transparent 98%, #000) left, radial-gradient(circle 5px at 100% 50%, transparent 98%, #000) right;
  -webkit-mask-size:51% 100%;-webkit-mask-repeat:no-repeat;
  mask:radial-gradient(circle 5px at 0 50%, transparent 98%, #000) left, radial-gradient(circle 5px at 100% 50%, transparent 98%, #000) right;
  mask-size:51% 100%;mask-repeat:no-repeat;
  animation:skSpool 1.9s ease-out forwards;}
#skullduggery-arcade .sk-stub b{font-size:.9rem;letter-spacing:.02em}
#skullduggery-arcade .sk-stub .sk-stub-brand{border-top:1px dashed rgba(15,11,8,.5);padding-top:2px;font-size:.5rem}
@keyframes skSpool{0%{transform:translateY(0) rotate(var(--sk-tilt,0deg));opacity:0}12%{opacity:1}55%{transform:translateY(-130px) rotate(var(--sk-tilt,0deg));opacity:1}100%{transform:translateY(-190px) rotate(var(--sk-tilt,0deg));opacity:0}}
@media (prefers-reduced-motion:reduce){#skullduggery-arcade .sk-stub{animation:none;display:none}}

#skullduggery-arcade #sk-toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(20px);background:var(--sk-parch-raised);border:1px solid var(--sk-gold);border-radius:3px;color:var(--sk-gold-bright);font-family:'JetBrains Mono',monospace;font-size:.72rem;letter-spacing:.1em;padding:10px 18px;opacity:0;transition:opacity .2s, transform .2s;z-index:9999;pointer-events:none;text-transform:uppercase;}
#skullduggery-arcade #sk-toast.show{opacity:1;transform:translateX(-50%) translateY(0)}

@media (max-width:560px){
  #skullduggery-arcade .sk-lock-wrap{gap:8px}
  #skullduggery-arcade .sk-pin-col{width:44px}
  #skullduggery-arcade .sk-pin-track{width:28px;height:180px}
  #skullduggery-arcade .sk-seal-desk{grid-template-columns:repeat(2,100px)}
  #skullduggery-arcade .sk-seal{width:100px;height:100px}
}

/*  Dashing Mini-Game Tab  */

/* Card-grid picker (replaced the old inline tab-strip 2026-07-25) —
   clicking a card opens the game full-screen in #dashing-overlay,
   same "leave the page to go play" pattern as the Gambling Den's
   #gambling-den .overlay/gdOpenGame. */
.dashing-game-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:0.6rem;}

.dashing-game-card{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0.35rem;padding:0.9rem 0.5rem;border-radius:10px;background:var(--bg2);border:1px solid var(--bg3);color:var(--text);cursor:pointer;transition:border-color 0.15s,transform 0.1s;text-align:center;}

.dashing-game-card i{font-size:1.5rem;color:var(--gold);}

.dashing-game-card .dgc-name{font-size:0.78rem;font-weight:700;}

.dashing-game-card .dgc-sub{font-size:0.62rem;color:var(--text2);}

.dashing-game-card:hover{border-color:var(--gold);}

.dashing-game-card:active{transform:scale(0.96);background:rgba(255,215,0,0.06);}

.dashing-frame{display:none;width:100%;border:none;height:680px;border-radius:8px;flex-shrink:0;}

.dashing-frame.active{display:block;}

/* Snake + Frogger render a d-pad below the canvas now that touch
   gating is pointer-based instead of viewport-based. The iframe
   needs the extra height or the pad gets clipped by the iframe
   boundary and looks like it's hiding behind the page chrome. */
#dgframe-snake,#dgframe-frogger,#dgframe-sudoku{height:800px;}

@media(max-width:480px){.dashing-frame{height:580px;}#dgframe-snake,#dgframe-frogger,#dgframe-sudoku{height:700px;}}

/* DD Inc (idle) is content-driven, not a fixed-size canvas like the
   other 4 Dashing games -- a guessed pixel height (680/580px above)
   left a gap below the game on any screen taller than the guess,
   made worse on mobile in-app browsers whose toolbar collapses AFTER
   the page has already loaded (no reliable resize event to react to).
   Flex-fill it against the real available space instead, so it
   tracks the viewport natively with no JS involved. flex-shrink:0 on
   .dashing-frame above keeps the other 4 games' tuned pixel heights
   exactly as before -- they still overflow-scroll .dg-stage rather
   than getting compressed. */
#dgframe-idle{height:auto;flex:1;min-height:0;}

/* Delve Deep / Delve Deep 2 are full RPG pages (header, maze grid,
   combat screen, footer) inside the same fixed-height iframe budget
   as the simple canvas games -- the guessed 680/580px cut them off
   short inside the floating #dashing-overlay modal instead of using
   the room actually available. Same flex-fill treatment as DD Inc.
   above: track the real available height of the modal instead of a
   guessed pixel number. */
#dgframe-delve,#dgframe-delve2{height:auto;flex:1;min-height:0;}

/* Full-screen game overlay — mirrors #gambling-den .overlay/.overlay.open
   (rw-core.css ~5060) but themed with the app's own tokens instead of
   Gambling Den's parchment skin, since Dashing isn't a separate reskinned
   sub-app. --sat (safe-area-inset-top) applied to the bar the same way
   the topbar handles notches, since this is another fixed full-viewport
   element. */
#dashing-overlay{position:fixed;inset:0;background:var(--bg0);z-index:9999;display:none;flex-direction:column;}

#dashing-overlay.open{display:flex;}

#dashing-overlay .dg-bar{display:flex;align-items:center;gap:0.6rem;flex-shrink:0;padding:calc(0.6rem + var(--sat)) 0.8rem 0.6rem;border-bottom:1px solid var(--bg3);background:var(--bg1);}

#dashing-overlay .dg-back{background:none;border:1px solid var(--bg3);color:var(--text);border-radius:6px;padding:0.35rem 0.6rem;font-size:0.8rem;cursor:pointer;flex-shrink:0;}

#dashing-overlay .dg-title{flex:1;min-width:0;font-weight:700;font-size:0.9rem;color:var(--gold);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

#dashing-overlay .dg-stage{flex:1;overflow-y:auto;scrollbar-gutter:stable;padding:0.6rem;display:flex;flex-direction:column;}

#dashing-overlay #dashing-frame-wrap{margin-top:0;flex:1;display:flex;flex-direction:column;min-height:0;}

/*  Peer Review  */

.pr-cat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:0.5rem;margin-bottom:0.75rem;}

.pr-cat-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0.3rem;padding:0.75rem 0.4rem;border:2px solid var(--bg3);border-radius:10px;background:var(--bg2);color:var(--text2);font-size:0.68rem;font-weight:700;cursor:pointer;transition:all 0.15s;text-align:center;user-select:none;-webkit-tap-highlight-color:transparent;}

.pr-cat-btn i{font-size:1.2rem;}

.pr-cat-btn.selected{border-color:var(--gold);color:var(--gold);background:rgba(255,215,0,0.09);}

.pr-cat-btn.disabled{opacity:0.45;cursor:default;}

.pr-player-block{background:var(--bg2);border:1px solid var(--bg3);border-radius:10px;padding:0.75rem;margin-bottom:0.6rem;}

.pr-player-name{font-weight:700;font-size:0.85rem;margin-bottom:0.5rem;}

.pr-cat-row{display:flex;align-items:center;justify-content:space-between;margin-bottom:0.4rem;}

.pr-cat-label{font-size:0.72rem;color:var(--text2);flex:1;}

.pr-stars{display:flex;gap:0.15rem;}

.pr-star{font-size:1.1rem;cursor:pointer;color:var(--bg3);transition:color 0.1s;-webkit-tap-highlight-color:transparent;}

.pr-star.lit{color:var(--gold);}

.pr-progress-row{display:flex;align-items:center;gap:0.6rem;margin-bottom:0.3rem;}

.pr-progress-bar{flex:1;height:7px;background:var(--bg3);border-radius:4px;overflow:hidden;}

.pr-progress-fill{height:100%;background:var(--gold);border-radius:4px;transition:width 0.4s;}

.pr-result-row{display:flex;justify-content:space-between;align-items:center;padding:0.4rem 0;border-bottom:1px solid var(--bg3);font-size:0.82rem;}

.pr-result-score{font-weight:800;color:var(--gold);font-size:1rem;}

/*  NPC Companions  */

/*    Cost of Living Survey    */

.col-backdrop{position:fixed;inset:0;background:rgba(0,0,0,0.75);z-index:9999;display:flex;align-items:center;justify-content:center;padding:1rem;}

.col-modal{background:var(--bg1);border:1px solid var(--bg3);border-radius:16px;padding:1.5rem;width:100%;max-width:480px;max-height:90dvh;overflow-y:auto;}

.col-modal h2{font-size:1.1rem;font-weight:800;color:var(--gold);margin-bottom:0.25rem;}

.col-modal .col-subtitle{font-size:0.75rem;color:var(--text2);margin-bottom:1.25rem;}

.col-section{margin-bottom:1.1rem;}

.col-section-label{font-size:0.72rem;text-transform:uppercase;letter-spacing:0.08em;color:var(--text2);margin-bottom:0.45rem;font-weight:700;}

.col-options{display:grid;grid-template-columns:1fr 1fr;gap:0.5rem;}

.col-opt{display:flex;flex-direction:column;align-items:center;text-align:center;gap:0.25rem;padding:0.75rem 0.5rem;border:2px solid var(--bg3);border-radius:12px;background:var(--bg2);cursor:pointer;transition:all 0.15s;-webkit-tap-highlight-color:transparent;}

.col-opt:hover{border-color:var(--gold);background:rgba(255,215,0,0.06);transform:translateY(-1px);}

.col-opt.selected{border-color:var(--gold);background:rgba(255,215,0,0.1);color:var(--gold);}

.col-opt .col-opt-icon{font-size:1.8rem;line-height:1;margin-bottom:0.1rem;}

.col-opt .col-opt-name{font-size:0.78rem;font-weight:700;line-height:1.2;}

.col-opt .col-opt-desc{font-size:0.65rem;color:rgba(255,255,255,0.4);line-height:1.35;}

.col-opt.selected .col-opt-desc{color:rgba(255,215,0,0.6);}

.col-opt .col-opt-cost{font-size:0.72rem;font-weight:700;color:var(--gold);margin-top:0.2rem;}

.col-opt.selected .col-opt-cost{color:var(--gold);}

.col-opt .col-opt-stash{font-size:0.62rem;font-weight:600;color:#b482ff;margin-top:0.1rem;opacity:0.85;}

.col-opt.selected .col-opt-stash{color:#b482ff;opacity:1;}

.col-opt .col-opt-credit{font-size:0.62rem;font-weight:600;color:#e0a030;margin-top:0.1rem;opacity:0.85;}

.col-opt.selected .col-opt-credit{color:#e0a030;opacity:1;}

.col-total-bar{display:flex;justify-content:space-between;align-items:center;background:var(--bg2);border:1px solid var(--bg3);border-radius:10px;padding:0.65rem 0.9rem;margin-bottom:1rem;}

.col-total-label{font-size:0.78rem;color:var(--text2);}

.col-total-amount{font-size:1.4rem;font-weight:800;color:var(--gold);}

#ptab-dashing.col-locked .col-lock-icon{display:inline;}

#ptab-dashing .col-lock-icon{display:none;font-size:0.7rem;color:var(--text2);margin-left:3px;}

/*    Cost of Living Chart    */

.col-chart-svg{width:100%;height:100%;}

.col-chart-line{fill:none;stroke:#ff6b35;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}

.col-chart-area{fill:url(#colChartGradient);opacity:0.3;}

.col-chart-grid{stroke:var(--bg3);stroke-width:1;stroke-dasharray:2,2;}

.col-chart-point{fill:#ff6b35;stroke:var(--bg1);stroke-width:2;}

.col-chart-point:hover{fill:var(--gold);r:5;}

.col-chart-axis{stroke:var(--bg3);stroke-width:1;}

.col-chart-label{font-size:9px;fill:var(--text2);}

.col-chart-tooltip{position:absolute;background:var(--bg1);border:1px solid var(--bg3);border-radius:6px;padding:0.4rem 0.6rem;font-size:0.75rem;pointer-events:none;z-index:10;display:none;box-shadow:0 4px 12px rgba(0,0,0,0.3);}

.col-chart-tooltip::after{content:'';position:absolute;bottom:-5px;left:50%;transform:translateX(-50%);border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid var(--bg3);}

@keyframes pulse{0%,100%{opacity:1;}50%{opacity:0.6;}}

@keyframes vivAvatarBounce{0%,100%{transform:translateY(0);}50%{transform:translateY(-2px);}}
.viv-avatar-8bit{image-rendering:pixelated;animation:vivAvatarBounce 1.6s ease-in-out infinite;border:2px solid var(--bg3);border-radius:4px;background:var(--bg2);}

.comp-card{background:var(--bg2);border:1px solid var(--bg3);border-radius:12px;padding:0.85rem;margin-bottom:0.6rem;transition:border-color 0.2s;}

.comp-card.low-loyalty{border-color:rgba(239,83,80,0.5);animation:compPulse 2.5s infinite;}

@keyframes compPulse{0%,100%{box-shadow:none;}50%{box-shadow:0 0 10px rgba(239,83,80,0.3);}}

.comp-card-header{display:flex;align-items:center;gap:0.75rem;margin-bottom:0.65rem;}

.comp-avatar{width:44px;height:44px;border-radius:10px;background:var(--bg3);display:flex;align-items:center;justify-content:center;font-size:1.4rem;flex-shrink:0;}

.comp-stat-row{display:flex;align-items:center;gap:0.5rem;margin-bottom:0.3rem;}

.comp-stat-label{font-size:0.65rem;color:var(--text2);text-transform:uppercase;letter-spacing:0.05em;width:52px;flex-shrink:0;}

.comp-stat-bar{flex:1;height:7px;background:var(--bg3);border-radius:4px;overflow:hidden;}

.comp-stat-fill{height:100%;border-radius:4px;transition:width 0.5s;}

.comp-stat-fill.high{background:var(--green);}

.comp-stat-fill.mid{background:var(--gold);}

.comp-stat-fill.low{background:var(--red);}

.comp-stat-val{font-size:0.68rem;color:var(--text2);width:26px;text-align:right;flex-shrink:0;}

.comp-actions{display:grid;grid-template-columns:repeat(3,1fr);gap:0.35rem;margin-top:0.6rem;}

.comp-action{display:flex;flex-direction:column;align-items:center;gap:0.2rem;padding:0.45rem 0.25rem;border:1px solid var(--bg3);border-radius:8px;background:var(--bg1);cursor:pointer;font-size:0.62rem;color:var(--text2);font-weight:700;transition:all 0.15s;-webkit-tap-highlight-color:transparent;}

.comp-action i{font-size:1rem;}

.comp-action:disabled,.comp-action.on-cd{opacity:0.45;cursor:default;}

.comp-action.ready{border-color:rgba(255,215,0,0.4);color:var(--gold);}

.comp-stasis-badge{display:inline-flex;align-items:center;gap:0.3rem;font-size:0.68rem;color:var(--text2);background:rgba(255,255,255,0.05);border:1px solid var(--bg3);border-radius:20px;padding:0.15rem 0.5rem;}

/* ── Phase 1: Thriving / streak / mood badges on the comp-card ── */
.comp-thriving-badge{display:inline-flex;align-items:center;gap:0.3rem;font-size:0.65rem;color:#ffd166;background:linear-gradient(135deg,rgba(255,209,102,0.15),rgba(255,150,30,0.08));border:1px solid rgba(255,209,102,0.4);border-radius:20px;padding:0.18rem 0.55rem;font-weight:700;text-transform:uppercase;letter-spacing:0.04em;animation:thrivePulse 2.4s ease-in-out infinite;}
@keyframes thrivePulse{0%,100%{box-shadow:0 0 0 rgba(255,209,102,0);}50%{box-shadow:0 0 12px rgba(255,209,102,0.45);}}
.comp-streak-badge{display:inline-flex;align-items:center;gap:0.25rem;font-size:0.62rem;color:#ff9b6b;background:rgba(255,140,80,0.1);border:1px solid rgba(255,140,80,0.25);border-radius:20px;padding:0.14rem 0.5rem;font-weight:700;}
.comp-mood-emoji{font-size:1rem;margin-left:0.35rem;display:inline-block;}
.comp-meta-row{display:flex;flex-wrap:wrap;gap:0.3rem;align-items:center;margin-top:0.25rem;}

/* ── Discover a Recipe — chalkboard specials-board theme (2026-07-14) ── */
.rd-board{background:radial-gradient(ellipse at top, #2e3a2e 0%, #1b221b 100%);border:10px solid #4a3423;border-radius:6px;box-shadow:inset 0 0 50px rgba(0,0,0,0.55), 0 6px 18px rgba(0,0,0,0.4);font-family:'Kalam',cursive;color:#eeeadd;}
.rd-board-header{border-bottom:2px dashed rgba(238,234,221,0.3);}
.rd-board-title{font-family:'Permanent Marker',cursive;color:#fff8e0;text-shadow:0 0 8px rgba(255,255,255,0.12);letter-spacing:0.02em;}
.rd-board-sub{font-family:'Kalam',cursive;font-style:italic;color:#c9c4a8;}
.rd-section-label{font-family:'Permanent Marker',cursive;color:#ffd640;font-size:0.8rem;letter-spacing:0.03em;border-bottom:2px dashed rgba(238,234,221,0.3);padding-bottom:0.2rem;margin:0.75rem 0 0.4rem;text-transform:uppercase;}
.rd-chip{font-family:'Kalam',cursive;font-weight:700;background:transparent;border:2px solid rgba(238,234,221,0.4);color:#eeeadd;border-radius:6px;padding:0.28rem 0.65rem;font-size:0.78rem;cursor:pointer;transition:border-color 0.15s,background 0.15s;}
.rd-chip:hover{border-color:#eeeadd;}
.rd-chip.selected{background:rgba(255,214,64,0.16);border-color:#ffd640;color:#ffd640;box-shadow:0 0 8px rgba(255,214,64,0.3);}
.rd-chalk-hr{border:none;border-top:2px dashed rgba(238,234,221,0.25);margin:0.6rem 0;}
.rd-outcome{font-family:'Kalam',cursive;}
.rd-outcome-title{font-family:'Permanent Marker',cursive;}

/* ── D&D weapon/armor tag-chip filter + card pills (2026-07-14) ── */
.tag-chip{font-weight:600;background:var(--bg2);border:1px solid var(--bg4);color:var(--text2);border-radius:20px;padding:0.2rem 0.6rem;font-size:0.7rem;cursor:pointer;transition:border-color 0.15s,background 0.15s,color 0.15s;}
.tag-chip:hover{border-color:var(--gold);}
.tag-chip.selected{background:rgba(255,209,102,0.16);border-color:var(--gold);color:var(--gold);}
.tag-pill{font-size:0.6rem;font-weight:600;background:var(--bg3);color:var(--text2);border-radius:10px;padding:0.06rem 0.4rem;white-space:nowrap;}

/* ── Care mini-game modal ─────────────────────── */
.care-modal-bg{position:fixed;inset:0;background:rgba(0,0,0,0.85);z-index:9999;display:flex;align-items:center;justify-content:center;padding:1rem;}
.care-modal{background:var(--bg1);border:1px solid var(--bg3);border-radius:14px;padding:1.1rem;max-width:360px;width:100%;}
.care-modal-title{font-family:'Cinzel',serif;font-size:0.95rem;color:var(--gold);text-align:center;margin-bottom:0.35rem;letter-spacing:0.06em;}
.care-modal-sub{font-size:0.78rem;color:var(--text2);text-align:center;margin-bottom:0.85rem;}
.care-pick-grid{display:grid;grid-template-columns:1fr 1fr;gap:0.5rem;margin-bottom:0.75rem;}
.care-pick{background:var(--bg2);border:1px solid var(--bg3);border-radius:8px;padding:0.6rem 0.5rem;cursor:pointer;text-align:center;font-size:0.78rem;color:var(--text);transition:all 0.15s;}
.care-pick:hover{border-color:var(--gold);background:var(--bg3);transform:translateY(-1px);}
.care-pick .care-pick-icon{font-size:1.4rem;margin-bottom:0.2rem;}
.care-pick.discovered{border-color:#ffd166;background:linear-gradient(135deg,rgba(255,209,102,0.08),var(--bg2));}
.care-pick.discovered::after{content:'★';color:#ffd166;font-size:0.7rem;margin-left:0.3rem;}

.care-game{position:relative;background:var(--bg2);border:1px solid var(--bg3);border-radius:8px;padding:0.85rem;text-align:center;margin-bottom:0.6rem;}
.care-game-bar{position:relative;height:36px;background:var(--bg3);border-radius:6px;overflow:hidden;margin:0.6rem 0 0.5rem;}
.care-game-target{position:absolute;top:0;height:100%;background:linear-gradient(90deg,rgba(76,175,80,0.35),rgba(76,175,80,0.55),rgba(76,175,80,0.35));border-left:2px solid var(--green);border-right:2px solid var(--green);}
.care-game-cursor{position:absolute;top:0;width:6px;height:100%;background:var(--gold);box-shadow:0 0 8px rgba(255,215,0,0.6);transition:none;}
.care-game-hint{font-size:0.7rem;color:var(--text2);margin-top:0.3rem;}
.care-game-tap{margin-top:0.55rem;width:100%;padding:0.7rem;background:var(--gold);color:var(--bg1);border:none;border-radius:6px;font-weight:700;font-size:0.85rem;cursor:pointer;letter-spacing:0.04em;text-transform:uppercase;}
.care-game-tap:active{transform:scale(0.98);}
.care-game-skip{margin-top:0.4rem;width:100%;padding:0.45rem;background:transparent;color:var(--text2);border:1px solid var(--bg3);border-radius:6px;font-size:0.72rem;cursor:pointer;}
.care-game-result{font-size:0.95rem;font-weight:700;margin:0.4rem 0;}
.care-game-result.perfect{color:#ffd166;}
.care-game-result.good{color:var(--green);}
.care-game-result.miss{color:var(--text2);}

/* ── Phase 2: warnings, diary, gifts ─────────── */
.comp-card.thriving{border-color:rgba(255,209,102,0.4);box-shadow:0 0 14px rgba(255,209,102,0.12);}
.comp-warn-row{display:flex;flex-wrap:wrap;gap:0.3rem;margin:0.4rem 0 0.2rem;}
.comp-warn{font-size:0.65rem;color:#ff9b9b;background:rgba(255,90,90,0.1);border:1px solid rgba(255,90,90,0.3);border-radius:6px;padding:0.18rem 0.5rem;font-weight:700;display:inline-flex;align-items:center;gap:0.3rem;}
.comp-warn.crit{background:rgba(255,90,90,0.18);border-color:rgba(255,90,90,0.55);animation:warnPulse 1.6s ease-in-out infinite;}
@keyframes warnPulse{0%,100%{opacity:0.85;}50%{opacity:1;}}
.comp-extra-actions{display:flex;gap:0.3rem;flex-wrap:wrap;margin-top:0.4rem;}
.comp-extra-btn{font-size:0.65rem;padding:0.22rem 0.55rem;border:1px solid var(--bg3);background:var(--bg2);color:var(--text2);border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;gap:0.25rem;}
.comp-extra-btn:hover{border-color:var(--gold);color:var(--gold);}
.comp-extra-btn.gift{border-color:rgba(255,209,102,0.5);color:#ffd166;background:rgba(255,209,102,0.06);animation:thrivePulse 2.4s ease-in-out infinite;}
.comp-extra-btn.bond{border-color:rgba(255,107,107,0.5);color:#ff6b6b;background:rgba(255,107,107,0.06);}

.diary-list{max-height:50dvh;overflow-y:auto;padding-right:0.4rem;}
.diary-day{margin-bottom:0.7rem;}
.diary-day-head{font-size:0.72rem;color:var(--text2);font-weight:700;letter-spacing:0.05em;text-transform:uppercase;border-bottom:1px solid var(--bg3);padding-bottom:0.2rem;margin-bottom:0.3rem;}
.diary-row{display:flex;justify-content:space-between;align-items:center;padding:0.25rem 0.1rem;font-size:0.78rem;}
.diary-row .diary-act{color:var(--text);}
.diary-row .diary-meta{color:var(--text2);font-size:0.7rem;}
.diary-row.fav .diary-act::after{content:' ★';color:#ffd166;}
.diary-empty{font-size:0.78rem;color:var(--text2);text-align:center;padding:1rem;}

.comp-assigned-badge{display:inline-flex;align-items:center;gap:0.3rem;font-size:0.68rem;color:var(--purple);background:rgba(155,89,182,0.1);border:1px solid rgba(155,89,182,0.3);border-radius:20px;padding:0.15rem 0.5rem;}

/*    Tooltip Engine    */

#rw-tooltip{

  position:fixed;z-index:99999;pointer-events:none;

  background:rgba(18,18,28,0.97);color:#f0eaff;

  font-size:0.72rem;line-height:1.4;font-weight:400;

  padding:0.4rem 0.65rem;border-radius:7px;

  border:1px solid rgba(255,255,255,0.1);

  box-shadow:0 4px 18px rgba(0,0,0,0.55);

  max-width:220px;white-space:normal;word-break:break-word;

  opacity:0;transition:opacity 0.12s ease;

}

#rw-tooltip.visible{opacity:1;}

#rw-tooltip::after{

  content:'';position:absolute;left:50%;transform:translateX(-50%);

  border:5px solid transparent;

}

#rw-tooltip.tip-above::after{

  top:100%;border-top-color:rgba(18,18,28,0.97);

}

#rw-tooltip.tip-below::after{

  bottom:100%;border-bottom-color:rgba(18,18,28,0.97);

}

[data-tip]{cursor:default;}

[data-tip][onclick],[data-tip].btn{cursor:pointer;}

/* ===== Mobile Responsive ===== */

@media (max-width: 600px) {

  .topbar { padding: 0 0.75rem; gap: 0.4rem; }

  .topbar-logo { font-size: 0.88rem; }

  .topbar-role { display: none; }

  .topbar-user { font-size: 0.75rem; max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  .dashboard-body { padding: 0 0.6rem 0.6rem; }
  .dash-panel:not(:has(> .hero-subnav)) { padding-top: 0.6rem; }

  .login-card { padding: 1.25rem; }

  .auth-card  { padding: 1.25rem; }

  .modal { padding: 1.1rem; }

  .inbox-modal { max-height: 92dvh !important; }

  .role-tabs { grid-template-columns: 1fr 1fr; }

  .role-tab  { padding: 0.55rem 0.4rem; font-size: 0.82rem; }

  .card { padding: 0.85rem; }

  .page-title { font-size: 1.1rem; margin-bottom: 1rem; }

  .session-item { flex-wrap: wrap; }

  .finance-grid { grid-template-columns: 1fr 1fr; }

  .admin-tab { padding: 0.35rem 0.65rem; font-size: 0.74rem; }

  .agreement-row { grid-template-columns: 1fr; gap: 0.4rem; }

  .data-store-row { flex-direction: column; align-items: flex-start; }

  .ds-actions { margin-left: 0; }

  .ds-key { min-width: unset; }

  .inbox-panel { flex-direction: column; }

  .inbox-thread-list { width: 100%; max-height: 140px; border-right: none; border-bottom: 1px solid var(--bg3); }

  /* Tasks panel — mobile slide-stack. The default mobile layout
     squeezes the list into a 140px strip on top + detail below;
     for the review queue that crops both panes too aggressively.
     Switch to a swap: full-height list by default, full-height
     detail when [data-detail-open]. Slide via translateX so the
     transition feels native. Phase 2 polish (2026-06-05). */
  #dminbox-tasks-panel { position: relative; }
  #dminbox-tasks-panel > #dm-inbox-tasks-list,
  #dminbox-tasks-panel > .inbox-view {
    position: absolute; inset: 0;
    width: 100%; max-height: none;
    border-right: none; border-bottom: none;
    transition: transform 0.22s ease;
    will-change: transform;
  }
  /* Default: list on screen, detail parked off to the right. */
  #dminbox-tasks-panel > .inbox-view { transform: translateX(100%); }
  /* Detail open: list slides off-left, detail slides into view. */
  #dminbox-tasks-panel[data-detail-open="1"] > #dm-inbox-tasks-list { transform: translateX(-100%); }
  #dminbox-tasks-panel[data-detail-open="1"] > .inbox-view { transform: translateX(0); }
  /* Show the back-arrow bar inside the detail pane on mobile only —
     desktop keeps both panes visible side-by-side, no Back needed. */
  #dminbox-tasks-panel .inbox-task-backbar { display: flex !important; }

  #toast-container { left: 0.75rem; right: 0.75rem; bottom: 0.75rem; }

  /* Mobile: the combat modal is essentially full-screen, so lift
     toasts well clear of the roll footer (ca. 4.5rem with padding). */
  body:has(#modal-combat-ref.open) #toast-container,
  body.combat-modal-open #toast-container { bottom: 5rem; }

  .toast { max-width: 100%; }

  .dash-tab { font-size: 0.58rem; padding: 0.45rem 0.15rem; min-height: 48px; }

  .dash-tab i { font-size: 1rem; }

  /* DM tab bar has 9 tabs vs the player's 4 — flex:1 crushes each
     to ~40px on mobile and the labels become unreadable. Switch to
     horizontal scroll with a sane min-width per tab so labels stay
     legible and the user swipes to reach off-screen tabs. Same
     overflow-hidden-scrollbar pattern as .dash-subnav. */
  #dm-dash-tabs { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  #dm-dash-tabs::-webkit-scrollbar { display: none; }
  #dm-dash-tabs .dash-tab { flex: 0 0 auto; min-width: 70px; padding: 0.5rem 0.55rem; font-size: 0.62rem; }

  /* DM topbar campaign pill — without nowrap a long campaign name
     wraps onto multiple lines and pushes the inbox/chat/profile/
     logout buttons off the bar (visible on the "No Campaign Yet"
     placeholder which is ironically the longest copy). */
  #dm-topbar-campaign { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100px; flex-shrink: 1; }

  /* DM topbar action buttons — reduce padding so 4 icon buttons fit
     on the row with the logo + name without wrapping. */
  #screen-dm .topbar .btn-ghost.btn-sm { padding: 0.3rem 0.45rem; }

  .wiz-sline { max-width: 16px; }

  .wiz-step-lbl { min-width: 40px; font-size: 0.6rem; }

  .wiz-nav { flex-direction: column; gap: 0.5rem; }

  .wiz-nav > * { width: 100%; text-align: center; }

  .profile-header { gap: 0.6rem; }

  .profile-avatar { width: 44px; height: 44px; font-size: 1.2rem; }

  .profile-name { font-size: 1rem; }

  .pr-cat-grid { grid-template-columns: repeat(2, 1fr); }

  .col-modal { padding: 1rem; }

  .modal-footer { flex-wrap: wrap; }

  .modal-footer .btn { flex: 1; justify-content: center; }

}

@media (max-width: 380px) {

  .dashboard-body { padding: 0 0.4rem 0.4rem; }
  .dash-panel:not(:has(> .hero-subnav)) { padding-top: 0.4rem; }

  .topbar-user { display: none; }

  /* Hide the campaign pill on the tightest screens — it's also shown
     on the Board panel, so the topbar can give the icons more room. */
  #dm-topbar-campaign { display: none; }

  .finance-grid { grid-template-columns: 1fr; }

  .col-options { grid-template-columns: 1fr; }

  .dash-tab span { display: none; }

  .dash-tab { min-height: 44px; }

  .dash-tab i { font-size: 1.2rem; }

  /* Even with labels hidden, keep the DM tabs scrolling rather than
     squeezing 9 icons into the viewport. min-width drops so a few
     more icons fit per swipe. */
  #dm-dash-tabs .dash-tab { min-width: 56px; padding: 0.45rem 0.4rem; }

}

/* Support-the-DM footer — no longer fixed; flows with the page like the topbar */

/* ═══════════════════════════════════════════════════════════════════════
   MID-SIZE + PORTRAIT — 601 to 760px (small tablets, large phones
   in landscape, portrait Android tablets)
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 760px) {
  .topbar { padding: 0 0.85rem; gap: 0.5rem; }
  .dashboard-body { padding-left: 0.75rem; padding-right: 0.75rem; }
  .card { padding: 0.95rem; }
  /* DM map side-panel narrows before the full portrait collapse at 720px */
  .wm-side-panel { width: 200px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   PORTRAIT ORIENTATION — applies on top of width-based rules when the
   device is held vertically. Catches portrait tablets (600-1024px wide)
   that fall outside the narrow-phone (<600px) rules.
   ═══════════════════════════════════════════════════════════════════════ */
@media (orientation: portrait) and (max-width: 1024px) {
  /* World map: stack side panel below the map on portrait tablets
     (phones are already handled by the max-width: 720px rule). */
  .wm-body { flex-direction: column; gap: 0.4rem; }
  .wm-map-wrap { flex: 1; min-height: 55dvh; }
  .wm-side-panel { width: 100%; max-height: 35dvh; border-top: 1px solid var(--bg3); padding-top: 0.4rem; }

  /* Finance grid: 3 columns is fine at 768px portrait (each ~230px),
     but 2 columns is more readable — only apply below 700px to avoid
     changing the desktop look on tablets like iPad (768px). */
}

@media (orientation: portrait) and (max-width: 700px) {
  /* Wealth summary: 3-column strip is tight on a 6-inch screen held
     portrait; drop to 2 cols so numbers are legible. Targets
     .ws-kpi-row specifically now (2026-07-20) — the outer
     .wealth-sticky-summary became a flex column when the actions row
     was added, so grid-template-columns only makes sense on the inner
     KPI row now. */
  .wealth-sticky-summary .ws-kpi-row { grid-template-columns: repeat(2, 1fr); }
  /* top is intentionally NOT overridden here — the base top:148px already
     accounts for the full nav stack (topbar+tabs+subnav) at 600-700px.
     A previous top:92px was wrong: it only covered topbar+tabs and caused
     the summary to render under the subnav on 7-inch portrait tablets. */
}

/* ── Portrait: safe-area bottom inset ─────────────────────────────── */
@media (orientation: portrait) {
  /* Ensure home-bar (iPhone) never overlaps content. The dashboard-body
     already uses env(safe-area-inset-bottom) in its base rule; this
     explicit rule makes sure the override at <600px (padding-bottom: 5.5rem)
     doesn't eliminate it — 5.5rem already exceeds the 34px home bar on
     all current devices, so no further change is needed there. */
  .dashboard-body {
    padding-bottom: max(calc(1.5rem + env(safe-area-inset-bottom, 0)), 1.5rem);
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   MOBILE IMPROVEMENTS — unified quick wins
   Applied to viewports up to 600px. Focuses on: compact sticky nav,
   minimum 44px tap targets, horizontal-scrolling tables, readable text.
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 600px) {

  /* Footer can wrap to two rows on narrow screens — give it more breathing room.
     max() ensures we never lose the home-bar safe area on iPhones. */
  body { padding-bottom: max(5rem, calc(1rem + env(safe-area-inset-bottom, 0))); }
  .dashboard-body { padding-bottom: max(5.5rem, calc(1.5rem + env(safe-area-inset-bottom, 0))); }

  /* Slimmer sticky nav — reclaim screen real estate. Math:
     topbar (46) + dash-tabs (44 + 2 border = 46) = 92 → subnav top
     pins to 92 to seam flush with the tab bar's bottom edge. The
     hero-subnav uses the same offset; .wealth-sticky-summary stacks
     a further ~42 px subnav below at 134. Previous values left an
     8 px gap at <380 px because the offsets were copy-pasted from
     the 600 px breakpoint without recomputing for the shorter rows. */
  .topbar { height: calc(46px + var(--sat)); }
  .dash-tabs { top: calc(46px + var(--sat)); }
  .dash-tab { min-height: 44px; padding: 0.3rem 0.15rem; }
  .dash-subnav { top: calc(92px + var(--sat)); padding: 0.3rem 0.5rem; }
  .hero-subnav { top: calc(92px + var(--sat)); }
  .wealth-sticky-summary { top: calc(134px + var(--sat)); }
  #research-active-training { top: calc(134px + var(--sat)); }
  .dash-subtab { padding: 0.25rem 0.55rem; font-size: 0.68rem; }

  /* Tables must scroll horizontally, never overflow the page */
  table { font-size: 0.78rem; min-width: max-content; }
  .card table, .dashboard-body table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
  table.no-mobile-scroll { display: table; overflow: visible; }

  /* Modals — edge-to-edge, usable padding */
  .modal { padding: 1rem; border-radius: 10px; }
  /* The player's mailbox uses #modal-inbox with class .inbox-modal.
     On mobile we want it truly full-screen so the compose bar isn't
     pushed below iOS chrome. height:100dvh uses the dynamic viewport
     height (recomputes when chrome appears/disappears), unlike 94vh
     which is locked to the largest-possible viewport. Remove the
     backdrop padding for #modal-inbox so the modal can claim every
     pixel; force overflow:hidden !important so child .inbox-messages
     is the only scrollable region (no rubber-band on the modal itself). */
  #modal-inbox.open { padding: 0; align-items: stretch; }
  .inbox-modal {
    max-width: 100% !important;
    max-height: 100dvh !important;
    height: 100dvh !important;
    border-radius: 0 !important;
    overflow: hidden !important;
  }
  .col-modal { padding: 0.85rem; }

  /* Minimum 44px tap targets for anything clickable */
  button, .btn, .btn-sm, select, input[type="button"], input[type="submit"] { min-height: 44px; }
  .btn-sm { padding: 0.5rem 0.75rem; font-size: 0.8rem; }
  input[type="text"], input[type="number"], input[type="email"], input[type="password"], input[type="tel"], input[type="search"], input[type="date"], textarea, select.form-input { min-height: 44px; font-size: 16px; } /* 16px prevents iOS zoom on focus */

  /* Wizard point-buy / stat adjust buttons were 22px — too small */
  .wiz-pb-adj button, .wiz-sa-grid button, .wiz-man-grid button, .wiz-stats-grid button { width: 36px !important; height: 36px !important; font-size: 1rem !important; }

  /* Tiny caption / meta text — bump floor to ~12px so it's still readable */
  .id-badge, .json-preview, .activity-time, .wiz-preview-stat { font-size: 0.78rem; }

  /* Forms spacing — less cramped */
  .form-input { padding: 0.55rem 0.7rem; }
  .form-label { font-size: 0.8rem; }

  /* Long text truncation with ellipsis so flex rows don't blow out */
  .topbar-logo { font-size: 0.82rem; letter-spacing: 0.05em; }

  /* Modal footer — full-width stacked buttons on very narrow screens */
  .modal-footer .btn { min-height: 44px; }
}

@media (max-width: 380px) {
  /* Extra-tight: sacrifice logo letter-spacing, slimmer padding */
  .topbar { padding: 0 0.5rem; }
  .topbar-logo { letter-spacing: 0; font-size: 0.78rem; }
  .modal { padding: 0.75rem; }
  .card { padding: 0.65rem; }
  .page-title { font-size: 0.98rem; margin-bottom: 0.7rem; }
}

.profiles-subtab { padding: 0.35rem 0.9rem; border-radius: 6px 6px 0 0; border: 1px solid transparent; background: transparent; color: var(--text2); cursor: pointer; font-size: 0.82rem; transition: all 0.15s; }

.profiles-subtab.active { background: var(--bg2); border-color: var(--bg3); color: var(--text); font-weight: 600; }

.profiles-subtab:hover:not(.active) { color: var(--text); background: rgba(255,255,255,0.04); }

/* == Wizard Live Preview == */

#wiz-preview { background:linear-gradient(135deg,rgba(255,215,0,0.07) 0%,transparent 100%); border:1px solid rgba(255,215,0,0.22); border-radius:var(--radius); padding:0.55rem 0.85rem; margin:0.5rem 0 0.75rem; display:flex; align-items:center; gap:0.6rem; flex-wrap:wrap; min-height:2.4rem; position:sticky; top:0; z-index:10; backdrop-filter:blur(4px); }

.wiz-preview-name { font-weight:700; font-size:0.88rem; color:var(--gold); }

.wiz-preview-sep  { color:var(--bg4); font-size:0.78rem; }

.wiz-preview-tag  { font-size:0.78rem; color:var(--text2); }

.wiz-preview-stats { display:flex; gap:0.6rem; margin-left:auto; flex-wrap:wrap; }

.wiz-preview-stat { font-size:0.72rem; background:var(--bg2); border-radius:4px; padding:0.15rem 0.45rem; color:var(--text); display:flex; gap:0.25rem; align-items:center; }

.wiz-preview-stat strong { color:var(--gold); }

/* == Equipment Kit Cards == */

.wiz-kit-grid { display:grid; grid-template-columns:1fr; gap:0.65rem; margin-bottom:0.75rem; }

.wiz-kit-card { background:var(--bg2); border:2px solid var(--bg3); border-radius:var(--radius); padding:0.75rem 1rem; cursor:pointer; transition:border-color 0.15s,background 0.15s; text-align:left; }

.wiz-kit-card:hover { border-color:rgba(255,215,0,0.4); }

.wiz-kit-card.wiz-selected { border-color:var(--gold); background:rgba(255,215,0,0.07); }

.wiz-kit-name { font-weight:700; font-size:0.9rem; color:var(--text); margin-bottom:0.2rem; }

.wiz-kit-items { font-size:0.78rem; color:var(--text2); line-height:1.6; }

/* == Spell Picker == */

.wiz-spell-grid { display:flex; flex-wrap:wrap; gap:0.45rem; margin-top:0.5rem; }

.wiz-spell-chip { font-size:0.72rem; border:1px solid var(--bg3); border-radius:20px; padding:0.2rem 0.6rem; cursor:pointer; background:var(--bg2); color:var(--text2); transition:all 0.12s; white-space:nowrap; }

.wiz-spell-chip:hover { border-color:var(--blue); color:var(--text); }

.wiz-spell-chip.wiz-selected { border-color:var(--blue); background:rgba(100,160,255,0.15); color:var(--blue); }

.wiz-spell-chip.wiz-full { opacity:0.4; cursor:default; }

.wiz-spell-chip.wiz-selected.wiz-full { opacity:1; }

/* ── Mobile: wizard modal ── */

@media (max-width: 600px) {

  /* Wizard full-height on mobile */

  #modal-wizard .modal {

    max-width: 100% !important;

    width: 100vw !important;

    max-height: 100dvh !important;

    border-radius: 0 !important;

    margin: 0 !important;

  }

  #modal-wizard {

    align-items: flex-end;

  }

  /* Preview strip wraps tighter */

  #wiz-preview { gap: 0.35rem; padding: 0.4rem 0.65rem; }

  .wiz-preview-stats { margin-left: 0; }

  /* Equipment kit grid: full width on mobile */

  .wiz-kit-card { padding: 0.55rem 0.75rem; }

  .wiz-kit-name { font-size: 0.82rem; }

  /* Spell chip grid wraps nicely */

  .wiz-spell-chip { font-size: 0.68rem; padding: 0.18rem 0.5rem; }

  /* Wizard ab-method tabs: 2 per row */

  .wiz-ab-method-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 0.4rem; }

  /* Wizard combat grid: full width */

  .wiz-combat-grid { grid-template-columns: 1fr 1fr; }

}

/* ── Mobile: inventory modal ── */

@media (max-width: 600px) {

  /* Backdrop should not eat any viewport space on mobile — the
     modal itself fills the full dynamic viewport.
     Not applied when the modal is portalled inline as a Hero sub-tab
     (.in-hero-sub) — inline mode lets the page handle scroll.
     #modal-shop shares this treatment — it reuses invtab-browse/
     invtab-amazon (unchanged since the Shop consolidation) plus the
     Merchant tab, and needs the exact same "whole modal scrolls"
     behavior those panels were built for. */
  #modal-inventory:not(.in-hero-sub), #modal-shop:not(.in-hero-sub) { padding: 0 !important; align-items: stretch !important; }

  /* Mobile layout: the whole modal becomes one scroll container and
     the tab bar sticks to the top. Previously the modal was a
     fixed-size flex column with only the tab panel scrolling, which
     made the huge loot-reports + coin-strip stack eat most of the
     viewport before the panel got a sliver at the bottom. Scrolling
     the entire modal as a page gives the Backpack / Amazon lists
     the whole screen once the user scrolls the headers off.
     The :not(.in-hero-sub) guard lets the inline-in-Hero override
     win back — otherwise this rule's ID specificity beats the
     .in-hero-sub override and traps scroll inside a 100dvh modal. */
  #modal-inventory:not(.in-hero-sub) .modal, #modal-shop:not(.in-hero-sub) .modal {
    max-width: 100% !important;
    width: 100vw !important;
    height: 100dvh !important;
    max-height: 100dvh !important;
    border-radius: 0 !important;
    display: block !important;           /* drop the flex-column chain */
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding-bottom: env(safe-area-inset-bottom);
  }
  #modal-inventory:not(.in-hero-sub) .modal > div:first-child, #modal-shop:not(.in-hero-sub) .modal > div:first-child { /* header */
    position: sticky; top: 0; z-index: 5;
    background: var(--bg1);
  }
  /* Tab bar row sits just below the sticky header so the Backpack /
     Shop / Amazon switcher stays reachable as the user scrolls the
     long modal. The 44px offset matches the sticky header height.
     Only applies in overlay mode — inline, the bar can scroll off. */
  #modal-inventory:not(.in-hero-sub) #inv-tabbar, #modal-shop:not(.in-hero-sub) #shop-tabbar {
    position: sticky;
    top: 44px;
    z-index: 4;
    background: var(--bg1);
  }
  /* Let the tab panels grow to their content now — no internal scroll
     on mobile, the modal itself handles scrolling. Components was added
     to the Shop modal after this rule was written and got missed — its
     panel kept fighting the modal for who owns the scroll, which is
     what broke mobile scrolling on that tab. Grocery had the exact same
     gap: added later, never folded into this rule. #inv-section-stash
     and #inv-section-equipped are the same shape (Backpack redesign's
     primary section switcher, not the older tab bar) and had the exact
     same gap — never folded into this rule either. */
  #invtab-mine, #invtab-browse, #invtab-amazon, #shoptab-merchant, #shoptab-components, #shoptab-grocery, #inv-section-stash, #inv-section-equipped {
    overflow: visible !important;
    max-height: none !important;
  }
  /* Components' own two-column body (list + favorites/spell sidebar) is a
     second nested scroll container — same fix, it must give up its own
     scroll and let its height grow so the modal is the only scroller.
     #comp-body-row is the row wrapping both columns; missing it here
     (it used to have no id to target at all) is what still trapped
     Components' scroll after the first pass at this fix.
     #merchant-list has always had its own overflow-y:auto too — it was
     never included in the original mobile fix (only its parent tab
     panel was), so Company Store had the exact same nested-scroll bug
     this whole time, just unreported until now. #grocery-list is the
     same shape as #merchant-list and has the identical bug.
     #inv-section-stash and #inv-section-equipped's own inner wrapper div
     (targeted here as "> div" since it has no id of its own) is the same
     shape again: an inline overflow-y:auto + overscroll-behavior:contain
     scroll body that, once its parent's flex-bounded height collapses on
     mobile, stretches to fit all its content and ends up with zero actual
     scroll range of its own — but overscroll-behavior:contain still
     refuses to let a touch gesture that starts inside it chain up to the
     .modal's own scroll, so the whole panel goes completely dead to touch.
     Confirmed via a real CDP-level touch-swipe test: 0 scroll before this
     rule, working scroll after. Stash was the one actually reported (its
     content is long enough to always need scrolling); Equipped has the
     identical markup shape and would hit the same wall the moment its
     content runs long on a small screen, so it's fixed here too rather
     than waiting for a second report. */
  #comp-body-row, #comp-list, #comp-sidebar, #merchant-list, #grocery-list, #inv-section-stash > div, #inv-section-equipped > div {
    overflow: visible !important;
    max-height: none !important;
  }
  #comp-list, #comp-sidebar { flex: 1 1 100% !important; }

  /* Header — tighter padding, trim title so the X button has room */
  #modal-inventory .modal > div:first-child { padding: 0.7rem 0.85rem !important; }
  #modal-inventory .modal > div:first-child h2 { font-size: 0.95rem !important; }

  /* Encumbrance + Equipped Gear strips: trim horizontal padding */
  #modal-inventory .modal > div:nth-child(2),
  #modal-inventory .modal > div:nth-child(3) { padding-left: 0.85rem !important; padding-right: 0.85rem !important; }

  /* Equipped Gear grid — 2 columns on phones so all 12 slots fit in
     6 rows instead of 12. Each slot gets tighter padding + smaller
     label font so the tile is scan-able without feeling cramped.
     <380px falls through to 1fr in the narrower block below. */
  .equip-slots-grid { grid-template-columns: 1fr 1fr !important; gap: 0.35rem !important; }
  .equip-slots-grid > * {
    padding: 0.45rem 0.55rem !important;
    min-height: 0 !important;
  }
  .equip-slots-grid .eq-slot-label,
  .equip-slots-grid [class*="eq-label"],
  .equip-slots-grid div:first-child > div:first-child { font-size: 0.58rem !important; }

  /* Equip picker row: select / name / button each full-width so they're tappable */
  #equip-slot-select, #equip-item-name { max-width: none !important; width: 100% !important; font-size: 16px !important; }

  /* Tab bar — bigger font + ensure 44px tap height */
  #invtab-btn-mine, #invtab-btn-log { font-size: 0.9rem !important; padding: 0.7rem 0.35rem !important; min-height: 44px !important; }

  /* Gold row fits on one line */

  .inv-gold-row { flex-wrap: wrap; gap: 0.35rem; }

  /* Browse items: remove description on mobile to save space */

  .item-card-desc { display: none; }

  /* Inventory modal inner padding shrinks on mobile */

  #invtab-mine, #invtab-browse { padding: 0.6rem !important; }

  /* Search + category filter row: stack full-width on narrow */
  #invtab-mine > div:first-child, #invtab-browse > div:first-child {
    gap: 0.4rem !important;
  }
  #inv-mine-search, #item-db-search { flex: 1 1 100% !important; min-width: 100% !important; font-size: 16px !important; }
  #inv-mine-cat, #item-db-cat, #item-db-edition { flex: 1 1 45% !important; max-width: none !important; font-size: 16px !important; }

  /* Shop/Backpack item cards — stack info over actions, make buttons full-row */
  #item-db-list > div, #inv-mine-list > div {
    padding: 0.7rem !important;
  }
  /* The row inside each card that holds cost + buy button — let it wrap to its own line */
  #item-db-list > div > div:last-child,
  #inv-mine-list > div > div:last-child {
    flex-wrap: wrap !important;
    gap: 0.4rem !important;
  }
  /* The buy/use buttons inside cards grow to fill the card width on very narrow */
  #item-db-list > div .btn-sm, #inv-mine-list > div .btn-sm { flex: 1 1 auto; min-height: 40px !important; }

  /* Single-column scroll — let the whole modal scroll instead of only
     the inner tab panel. On mobile the stacked top strips (encumbrance,
     equipped gear, loot reports, coins, tab bar) already eat most of a
     700px viewport; trapping the backpack inside another scroll context
     left barely 200px for the item list. Letting the outer modal scroll
     means the user sees a comfortable item column and can scroll up to
     revisit the strips.
     Inline-in-Hero mode (.in-hero-sub) skips this so the PAGE scroll
     carries the content — otherwise the modal becomes its own 100vh
     scroll container that the user can't reach past. */
  #modal-inventory:not(.in-hero-sub) .modal, #modal-shop:not(.in-hero-sub) .modal {
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
    /* Anchor the scroll to the header so the close button stays reachable. */
    overscroll-behavior: contain;
  }
  #invtab-mine, #invtab-browse, #shoptab-merchant, #shoptab-components, #shoptab-grocery, #comp-body-row, #merchant-list, #grocery-list {
    flex: none !important;
    overflow-y: visible !important;
    min-height: 0 !important;
  }
  /* Bigger, comfier equip chip on touch. */
  .bp-row .bp-row-equip-btn { padding: 0.45rem 0.75rem; font-size: 0.78rem; min-height: 36px; }

  /* Quest Log tab — the McDungeon hook list scrolls inside its box; a
     tapped hook opens the #modal-qb-quest reader (which scrolls on its
     own via the shared .modal rules), so there's no inline preview
     pane to wrestle with on mobile anymore. */
  #dmpanel-questlog #qb-list { max-height: 55dvh !important; }
  /* My Quests list scroller — the inline 65vh max is too tall for
     mobile where we want more room for the form above. */
  #dmpanel-questlog #dm-quests-list { max-height: 50dvh !important; }
}

/* Quest Edit Ticket modals */
.qt-overlay { position: fixed; inset: 0; z-index: 9000; background: rgba(0,0,0,0.6);
  display: flex; align-items: center; justify-content: center; padding: 1rem; }
.qt-box { background: var(--bg1); border: 1px solid var(--bg3); border-radius: 12px;
  padding: 1.1rem 1.2rem; width: min(94vw, 560px); max-height: 88dvh; overflow-y: auto;
  box-shadow: 0 18px 50px rgba(0,0,0,0.55); }

/* Floating (non-modal) window mode — desktop session-notes popup. The
   overlay stops blocking the page so the player can use the rest of the
   site; the box becomes a fixed, draggable, resizable panel. */
#session-notes-modal.floating { background: none; pointer-events: none; display: block; }
#session-notes-modal.floating .qt-box {
  pointer-events: auto; position: fixed; margin: 0;
  width: 420px; height: min(70dvh, 580px);
  min-width: 300px; min-height: 260px; max-width: 96vw; max-height: 94dvh;
  resize: both; overflow: auto; box-shadow: 0 12px 40px rgba(0,0,0,0.6); }
#sn-drag-handle { cursor: move; user-select: none; }

/* Rich-text note editor (word-processor toolbar + contenteditable) */
.rw-rt-toolbar { display:flex; flex-wrap:wrap; gap:0.2rem; align-items:center;
  border:1px solid var(--bg3); border-bottom:none; border-radius:6px 6px 0 0;
  background:var(--bg2); padding:0.3rem 0.35rem; }
.rw-rt-toolbar button { background:none; border:1px solid transparent; color:var(--text);
  width:28px; height:28px; border-radius:5px; cursor:pointer; font-size:0.8rem; line-height:1;
  display:inline-flex; align-items:center; justify-content:center; }
.rw-rt-toolbar button:hover { background:var(--bg3); }
.rw-rt-toolbar .rw-rt-sep { width:1px; height:18px; background:var(--bg3); margin:0 0.15rem; }
.rw-rt-editor { border:1px solid var(--bg3); border-radius:0 0 6px 6px; background:var(--bg0);
  min-height:8em; max-height:40dvh; overflow-y:auto; padding:0.5rem 0.6rem; font-size:0.84rem;
  line-height:1.5; color:var(--text); outline:none; }
.rw-rt-editor:empty:before { content:attr(data-placeholder); color:var(--muted); }
.rw-note-body ul, .rw-note-body ol { margin:0.3rem 0 0.3rem 1.25rem; padding:0; }
.rw-note-body h1,.rw-note-body h2,.rw-note-body h3,.rw-note-body h4 { margin:0.35rem 0 0.2rem; line-height:1.25; }
.rw-note-body h1 { font-size:1.15rem; } .rw-note-body h2 { font-size:1.05rem; } .rw-note-body h3 { font-size:0.95rem; }
.rw-note-body blockquote { border-left:3px solid var(--bg4); margin:0.3rem 0; padding:0.1rem 0.6rem; color:var(--text2); }
.rw-note-body a { color:var(--blue); text-decoration:underline; }
.rw-note-body p { margin:0.25rem 0; }

@media (max-width: 600px) {
  /* Touch-friendly sizing for coin buttons. */
  .inv-coin-btn { font-size: 0.82rem; min-height: 36px; }
}

/* ── Extra-narrow: <380px, keep single-column where it helps ── */
@media (max-width: 380px) {
  #modal-inventory .modal > div:first-child { padding: 0.6rem 0.75rem !important; }
  #modal-inventory .modal > div:first-child h2 { font-size: 0.88rem !important; }
  .equip-slots-grid { grid-template-columns: 1fr !important; }
  /* Tab labels shrink but icons keep the tap target */
  #invtab-btn-mine, #invtab-btn-log { font-size: 0.82rem !important; padding: 0.6rem 0.2rem !important; }
}

/* ── Looted Coin strip (works on desktop and mobile) ── */

.inv-coin-strip {

  display: flex; align-items: center; gap: 0.75rem; padding: 0.55rem 1rem;

  background: linear-gradient(135deg, rgba(255,215,0,0.06), transparent);

  border-top: 1px solid rgba(255,215,0,0.25);

  border-bottom: 1px solid rgba(255,215,0,0.25);

  flex-wrap: wrap;

}

.inv-coin-balance { display: flex; align-items: center; gap: 0.45rem; flex-shrink: 0; }

.inv-coin-disc {

  width: 30px; height: 30px; border-radius: 50%;

  background: radial-gradient(circle at 35% 35%, #ffe566, #c8860a);

  display: flex; align-items: center; justify-content: center;

  font-size: 0.85rem; font-weight: 900; color: #5a3a00;

  box-shadow: 0 2px 6px rgba(200,134,10,0.35);

  border: 2px solid rgba(255,215,0,0.6);

  flex-shrink: 0;

}

.inv-coin-add {

  display: flex; align-items: center; gap: 0.35rem;

  flex-wrap: wrap; flex: 1; min-width: 0;

}

/* The cp / sp / gp / pp buttons ride as one non-wrapping group so
   they can't stack vertically on narrow screens — the group itself
   can drop to a new line inside .inv-coin-add, but the four
   denominations stay side-by-side. */
.inv-coin-denoms {
  display: flex;
  flex-wrap: nowrap;
  gap: 0.3rem;
  flex-shrink: 0;
}
.inv-coin-denoms .inv-coin-btn { flex-shrink: 0; }

.inv-coin-amt-label {

  font-size: 0.6rem; color: var(--text2);

  text-transform: uppercase; letter-spacing: 0.05em;

  margin-right: 0.1rem;

}

.inv-coin-amt-input {

  width: 48px; height: 32px; padding: 0 0.35rem;

  border: 1px solid var(--bg3); background: var(--bg2);

  color: var(--text1); border-radius: 6px;

  font-size: 0.85rem; font-weight: 700; text-align: center;

  appearance: textfield;

}

.inv-coin-amt-input::-webkit-outer-spin-button,

.inv-coin-amt-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.inv-coin-btn {

  display: inline-flex; align-items: center; justify-content: center;

  min-width: 38px; height: 32px; padding: 0 0.55rem;

  border-radius: 6px; font-size: 0.78rem; font-weight: 700;

  cursor: pointer; transition: transform 0.08s, box-shadow 0.15s;

  border: 1px solid transparent;

}

.inv-coin-btn:active { transform: scale(0.96); }

.inv-coin-cp { background: #b87333; border-color: #8a5323; color: #fff; }

.inv-coin-sp { background: #c0c0c0; border-color: #888;    color: #222; }

.inv-coin-gp { background: var(--gold); border-color: #a07d08; color: #3a2400; }

.inv-coin-pp { background: #e5e4e2; border-color: #9a9a99; color: #222; }

.inv-coin-bulk {

  background: transparent; border: 1px solid var(--bg3);

  color: var(--text2);

}

.inv-coin-bulk:hover { border-color: var(--gold); color: var(--gold); }

/* Lone minus button — same footprint as the bulk button, tinted red so */
/* it reads as subtract without forcing a second row into the strip.   */
.inv-coin-minus {
  background: transparent; border: 1px solid var(--bg3);
  color: #c07070;
}
.inv-coin-minus:hover { border-color: #c04848; color: #ff8a8a; }

/* Brief gold flash + small scale bump on the DC counter when loot lands. */

@keyframes rwCoinFlash {

  0%   { color: var(--gold); transform: scale(1);   text-shadow: 0 0 0 rgba(255,215,0,0); }

  30%  { color: #ffe680;    transform: scale(1.18); text-shadow: 0 0 18px rgba(255,215,0,0.9); }

  100% { color: var(--gold); transform: scale(1);   text-shadow: 0 0 0 rgba(255,215,0,0); }

}

.rw-coin-flash {

  animation: rwCoinFlash 0.55s ease-out 1;

  display: inline-block;

}

/* ── Backpack redesign: sectioned layout + coin pouch + detail sheet ── */

.bp-container-tabs {

  display: flex; flex-wrap: wrap; gap: 0.3rem;

  margin-bottom: 0.55rem;

  padding-bottom: 0.55rem;

  border-bottom: 1px solid var(--bg3);

}

.bp-container-tab {

  display: inline-flex; align-items: center; gap: 0.35rem;

  padding: 0.4rem 0.7rem;

  background: var(--bg2); border: 1px solid var(--bg3);

  border-radius: 999px;

  color: var(--text2); font-size: 0.76rem; font-weight: 600;

  cursor: pointer; transition: all 0.15s;

}

.bp-container-tab:hover { border-color: var(--gold); color: var(--gold); }

.bp-container-tab.active {

  background: rgba(255,215,0,0.14);

  border-color: var(--gold);

  color: var(--gold);

}

.bp-container-tab-count {

  background: var(--bg3); color: var(--text2);

  border-radius: 999px; padding: 0 0.4rem;

  font-size: 0.62rem; font-weight: 700;

}

.bp-container-tab.active .bp-container-tab-count { background: rgba(255,215,0,0.2); color: var(--gold); }

.bp-container-add { background: transparent; border-style: dashed; }

.bp-container-delete { background: transparent; border-color: var(--red); color: var(--red); margin-left: auto; }

.bp-container-delete:hover { background: rgba(224,85,85,0.1); color: var(--red); border-color: var(--red); }

.bp-section {

  margin-bottom: 0.6rem;

  border: 1px solid var(--bg3);

  border-radius: 8px;

  background: var(--bg2);

  overflow: hidden;

}

.bp-section-header {

  display: flex; align-items: center; gap: 0.5rem;

  padding: 0.5rem 0.75rem;

  cursor: pointer; user-select: none;

  background: linear-gradient(90deg, var(--bg3), transparent);

  border-bottom: 1px solid var(--bg3);

  font-size: 0.78rem; font-weight: 700;

  color: var(--text1);

  text-transform: uppercase; letter-spacing: 0.05em;

}

.bp-section-header:hover { background: linear-gradient(90deg, rgba(255,215,0,0.1), transparent); }

.bp-section-header .bp-chev { margin-left: auto; font-size: 0.7rem; color: var(--text2); transition: transform 0.15s; }

.bp-section.collapsed .bp-chev { transform: rotate(-90deg); }

.bp-section.collapsed .bp-body { display: none; }

.bp-section-header .bp-count {

  font-size: 0.66rem; font-weight: 600; color: var(--text2);

  background: var(--bg3); padding: 0.1rem 0.45rem; border-radius: 999px;

  margin-left: 0.25rem;

}

.bp-section-header i.bp-icon { color: var(--gold); width: 16px; text-align: center; }

.bp-body { padding: 0.35rem 0.4rem 0.5rem; }

.bp-row {

  display: flex; align-items: center; gap: 0.55rem;

  padding: 0.5rem 0.65rem; margin-bottom: 0.3rem;

  background: var(--bg1); border: 1px solid var(--bg3);

  border-left-width: 3px;

  border-radius: 6px;

  cursor: pointer;

  transition: transform 0.08s, border-color 0.15s, background 0.15s;

}

.bp-row:hover, .bp-row:active { border-color: var(--gold); background: rgba(255,215,0,0.04); }

.bp-row:active { transform: scale(0.99); }

.bp-row .bp-row-icon { width: 22px; text-align: center; font-size: 1rem; flex-shrink: 0; }

.bp-row .bp-row-name { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.2; }

.bp-row .bp-row-title { font-weight: 700; font-size: 0.85rem; color: var(--text1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.bp-row .bp-row-sub { font-size: 0.66rem; color: var(--text2); display: flex; gap: 0.3rem; flex-wrap: wrap; }

.bp-row .bp-row-qty { flex-shrink: 0; font-size: 0.72rem; font-weight: 700; color: var(--gold); background: var(--bg3); padding: 0.1rem 0.4rem; border-radius: 999px; min-width: 28px; text-align: center; }

.bp-row .bp-row-equipped { flex-shrink: 0; font-size: 0.6rem; color: var(--gold); background: rgba(255,215,0,0.12); border: 1px solid rgba(255,215,0,0.4); padding: 0.1rem 0.4rem; border-radius: 999px; text-transform: uppercase; letter-spacing: 0.05em; }

/* One-tap equip chip inside each backpack row — shown for items with a
   suggested slot so the user doesn't have to open the detail sheet. */
.bp-row .bp-row-equip-btn {
  flex-shrink: 0; font-size: 0.7rem; font-weight: 700;
  color: var(--gold); background: rgba(255,215,0,0.1);
  border: 1px solid rgba(255,215,0,0.45); border-radius: 6px;
  padding: 0.3rem 0.6rem; cursor: pointer; line-height: 1;
  display: inline-flex; align-items: center; gap: 0.25rem;
}
.bp-row .bp-row-equip-btn:hover,
.bp-row .bp-row-equip-btn:active { background: rgba(255,215,0,0.2); }

/* Coin pouch — gilded section with four currency tiles */

.bp-coin-pouch { border-color: rgba(255,215,0,0.4); background: linear-gradient(135deg, rgba(255,215,0,0.06), transparent); }

.bp-coin-pouch .bp-section-header { background: linear-gradient(90deg, rgba(255,215,0,0.15), transparent); color: var(--gold); }

.bp-coin-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.4rem; padding: 0.6rem; }

.bp-coin-tile { display: flex; flex-direction: column; align-items: center; padding: 0.45rem 0.35rem; border-radius: 6px; border: 1px solid var(--bg3); background: var(--bg1); cursor: pointer; transition: border-color 0.15s; }

.bp-coin-tile:hover { border-color: var(--gold); }

.bp-coin-tile.empty { opacity: 0.45; }

.bp-coin-tile .bp-coin-icon { width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.72rem; font-weight: 900; margin-bottom: 0.2rem; }

.bp-coin-tile.cp .bp-coin-icon { background: radial-gradient(circle at 35% 35%, #e8946a, #8a5323); color: #fff; }

.bp-coin-tile.sp .bp-coin-icon { background: radial-gradient(circle at 35% 35%, #f0f0f0, #888); color: #222; }

.bp-coin-tile.gp .bp-coin-icon { background: radial-gradient(circle at 35% 35%, #ffe566, #c8860a); color: #5a3a00; }

.bp-coin-tile.pp .bp-coin-icon { background: radial-gradient(circle at 35% 35%, #f5f5f5, #bfbfbf); color: #222; }

.bp-coin-tile .bp-coin-qty { font-size: 0.95rem; font-weight: 800; color: var(--text1); }

.bp-coin-tile .bp-coin-label { font-size: 0.58rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.05em; }

.bp-coin-total { padding: 0 0.6rem 0.55rem; font-size: 0.68rem; color: var(--text2); display: flex; justify-content: space-between; }

.bp-coin-total strong { color: var(--gold); }

/* Item detail sheet — bottom sheet on mobile, centered modal on desktop */

.bp-detail-backdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.55); z-index: 10000; }

.bp-detail-backdrop.open { display: block; }

.bp-detail-sheet {

  position: fixed; left: 0; right: 0; bottom: 0;

  background: var(--bg1); border-top: 2px solid var(--gold);

  border-radius: 16px 16px 0 0;

  max-height: 85dvh; overflow-y: auto;

  padding: 1rem 1rem 1.25rem;

  z-index: 10001;

  transform: translateY(100%);

  transition: transform 0.25s ease-out, visibility 0s 0.25s;

  box-shadow: 0 -8px 32px rgba(0,0,0,0.5);

  display: none;

}

.bp-detail-sheet.open { display: block; transform: translateY(0); transition: transform 0.25s ease-out, visibility 0s; }

.bp-detail-sheet .bp-detail-header { display: flex; align-items: flex-start; gap: 0.65rem; margin-bottom: 0.75rem; padding-bottom: 0.65rem; border-bottom: 1px solid var(--bg3); }

.bp-detail-sheet .bp-detail-icon { font-size: 1.8rem; }

.bp-detail-sheet .bp-detail-title { flex: 1; min-width: 0; }

.bp-detail-sheet .bp-detail-title h3 { margin: 0 0 0.25rem; font-size: 1.05rem; color: var(--text1); }

.bp-detail-sheet .bp-detail-meta { font-size: 0.68rem; color: var(--text2); display: flex; gap: 0.4rem; flex-wrap: wrap; }

.bp-detail-sheet .bp-detail-close { background: transparent; border: 0; color: var(--text2); font-size: 1.1rem; padding: 0.1rem 0.4rem; cursor: pointer; }

.bp-detail-sheet .bp-detail-notes { font-size: 0.82rem; color: var(--text1); line-height: 1.5; padding: 0.55rem 0.75rem; background: var(--bg2); border-radius: 8px; margin-bottom: 0.75rem; }

.bp-detail-sheet .bp-detail-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 0.45rem; }

.bp-detail-sheet .bp-detail-actions button { padding: 0.6rem 0.55rem; font-size: 0.82rem; }

@media (min-width: 700px) {

  .bp-detail-sheet { left: 50%; right: auto; bottom: 50%; transform: translate(-50%, 120%); max-width: 520px; border-radius: 12px; border: 2px solid var(--gold); display: none; }

  .bp-detail-sheet.open { display: block; transform: translate(-50%, 50%); }

}

/* ── Mobile: spellbook modal ── */

@media (max-width: 600px) {

  /* :not(.in-hero-sub) keeps these rules from winning against the
     inline-Hero override — when Spellbook is a Hero sub-tab, let
     the page scroll naturally instead of pinning to 100dvh. */
  #modal-spellbook:not(.in-hero-sub) .modal {

    max-width: 100% !important;

    width: 100vw !important;

    max-height: 100dvh !important;

    border-radius: 0 !important;

  }

  #modal-spellbook:not(.in-hero-sub) { align-items: flex-end; }

}

/* ── Mobile: dash-tabs scroll instead of shrink ── */

@media (max-width: 600px) {

  .dash-tabs {

    overflow-x: auto;

    overflow-y: hidden;

    -webkit-overflow-scrolling: touch;

    scrollbar-width: none;

    flex-wrap: nowrap;

  }

  .dash-tabs::-webkit-scrollbar { display: none; }

  .dash-tab {

    flex-shrink: 0;

    min-width: 52px;

  }

}

/* ── Mobile: review grid single column ── */

@media (max-width: 480px) {

  .wiz-review-grid { grid-template-columns: 1fr !important; }

  #wiz-preview .wiz-preview-stats { display: none; } /* too crowded */

}

/* ── Portrait: tab bars that wrap on narrow screens ─────────────────── */

@media (max-width: 600px) {

  /* Dashing game tab bar: 5 buttons at min-width:80px exceed a 390px
     phone width and wrap to a 2nd row, adding ~44px of dead nav height
     above the game iframe. Horizontal scroll keeps them single-row.
     Same pattern as .dash-subnav and #dm-dash-tabs. */
  .dashing-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .dashing-tabs::-webkit-scrollbar { display: none; }
  .dashing-tab-btn { flex: 0 0 auto; }

  /* (forge-crafting-subtab-bar scroll fix moved to global rules below) */

}

/* ── Forge tab bars — always single-row horizontal scroll ────────────── */
#forge-main-tab-bar {
  flex-wrap: nowrap;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
#forge-main-tab-bar::-webkit-scrollbar { display: none; }
#forge-main-tab-bar .btn { flex: 0 0 auto; white-space: nowrap; }

/* ── Forge crafting strip — grouped by material family, each toolkit its
   own card (matches the Vivarium "Your Vivarium" pet-card grid) instead
   of a row of small pill buttons. ── */
.csub-group { display: flex; flex-direction: column; gap: 0.3rem; }
.csub-group.csub-group-empty { display: none; }
.csub-group-label {
  font-size: 0.6rem; font-weight: 700; color: var(--text2); text-transform: uppercase;
  letter-spacing: 0.06em; padding: 0 0.3rem;
}
.csub-group-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 0.4rem;
}
.csub-toolkit-card {
  cursor: pointer;
  margin-bottom: 0 !important;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.csub-toolkit-card .comp-card-header { margin-bottom: 0; }
.csub-toolkit-card .comp-avatar { width: 36px; height: 36px; font-size: 1.15rem; border-radius: 8px; }
.csub-toolkit-card .csub-toolkit-name { font-weight: 700; font-size: 0.78rem; line-height: 1.25; }
.csub-toolkit-card .csub-status-line { font-size: 0.64rem; color: var(--text2); margin-top: 0.1rem; }

/* ── Auction House — GW2 Trading Post-inspired redesign (2026-07-21) ──
   Buy/Sell tabs replace the old All/Mine toggle; a grid of icon tiles
   (rarity-colored border, tap to expand into a detail view) replaces
   the old stacked text card list on Buy; filters collapse into a
   sheet instead of always-on inline rows. Stays in RealmWatch's own
   --bg/--gold/--green tokens throughout — no new palette needed, since
   GW2's own Trading Post UI is already dark+gold. */
.auc-tabs { display: flex; gap: 0.4rem; margin-bottom: 0.65rem; border-bottom: 1px solid var(--bg3); }
.auc-tab {
  flex: 1; background: none; border: none; border-bottom: 2px solid transparent;
  color: var(--text2); font-family: var(--font-display); font-size: 0.82rem; font-weight: 700;
  letter-spacing: 0.04em; padding: 0.5rem 0.4rem; cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.auc-tab i { margin-right: 0.35rem; }
.auc-tab:hover { color: var(--text); }
.auc-tab.active { color: var(--gold); border-bottom-color: var(--gold); }

.auc-filter-toggle-btn {
  position: relative; display: flex; align-items: center; gap: 0.35rem; white-space: nowrap;
  background: var(--bg2); border: 1px solid var(--bg3); color: var(--text2);
  font-size: 0.72rem; padding: 0 0.65rem; border-radius: 6px; cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.auc-filter-toggle-btn:hover { color: var(--text); border-color: var(--bg4); }
.auc-filter-toggle-btn.active { color: var(--gold); border-color: rgba(255,215,0,0.4); background: rgba(255,215,0,0.06); }
.auc-filter-badge {
  display: inline-flex; align-items: center; justify-content: center; min-width: 15px; height: 15px;
  background: var(--gold); color: #1a1a10; font-size: 0.6rem; font-weight: 800; border-radius: 8px; padding: 0 3px;
}
.auc-filter-panel {
  background: var(--bg1); border: 1px solid var(--bg3); border-radius: 8px;
  padding: 0.6rem 0.7rem; margin-bottom: 0.65rem; animation: auc-sheet-open 0.18s ease;
}
@keyframes auc-sheet-open { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) { .auc-filter-panel { animation: none; } }

.auc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 0.5rem; }
.auc-tile {
  display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
  background: var(--bg1); border: 1px solid var(--bg3); border-radius: 9px;
  padding: 0.6rem 0.4rem 0.5rem; cursor: pointer; font-family: inherit; text-align: center;
  transition: transform 0.1s, box-shadow 0.15s; color: inherit;
}
.auc-tile:hover { transform: translateY(-2px); box-shadow: 0 6px 16px -8px rgba(0,0,0,0.6); }
.auc-tile:active { transform: translateY(0) scale(0.98); }
.auc-tile-icon {
  width: 40px; height: 40px; border-radius: 50%; border: 1.5px solid;
  display: flex; align-items: center; justify-content: center; font-size: 1.1rem;
}
.auc-tile-name {
  font-size: 0.68rem; font-weight: 600; color: var(--text); line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 1.7em;
}
.auc-tile-price { font-size: 0.72rem; font-weight: 800; color: var(--green); }

.auc-sell-row {
  display: flex; align-items: center; gap: 0.6rem; background: var(--bg1);
  border: 1px solid var(--bg3); border-radius: 8px; padding: 0.5rem 0.6rem; margin-bottom: 0.4rem;
}
.auc-sell-icon {
  width: 34px; height: 34px; border-radius: 8px; border: 1.5px solid; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; font-size: 0.9rem;
}
.auc-sell-info { min-width: 0; flex: 1; }
.auc-sell-name { font-size: 0.82rem; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.auc-sell-meta { font-size: 0.64rem; color: var(--text2); margin-top: 0.1rem; }
.auc-sell-price { font-size: 0.8rem; font-weight: 700; color: var(--green); white-space: nowrap; flex-shrink: 0; }

.auc-detail-card { background: var(--bg1); border: 1px solid var(--bg3); border-radius: 10px; padding: 0.85rem; }

