
    /* Page-local styles. Colour comes entirely from tokens.css; this block is
       layout, type and motion. The component recipes (.site-header, .wordmark,
       .btn, .field, dialog, .card-close, .pw-toggle) are carried over from the
       landing verbatim so the dashboard reads as the same product. Inline
       <style> is CSP-permitted (style-src). */

    :root {
      --maxw: 68rem;
      --pad: clamp(1rem, 4vw, 3rem);
      --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      --ease: cubic-bezier(0.22, 1, 0.36, 1);
      /* d20 glyph reused as a mask so marks follow theme colour. */
      --d20: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 3 L27 9.5 L27 22.5 L16 29 L5 22.5 L5 9.5 Z M16 3 L16 29 M5 9.5 L27 22.5 M27 9.5 L5 22.5' fill='none' stroke='black' stroke-width='1.6'/%3E%3C/svg%3E");
      /* pencil glyph, used as a mask for the avatar edit overlay */
      --edit-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M4 16.5 L14.5 6 L18 9.5 L7.5 20 L4 20 Z M13 7.5 L16.5 11' fill='none' stroke='black' stroke-width='1.8' stroke-linejoin='round' stroke-linecap='round'/%3E%3C/svg%3E");
      /* padlock glyph, used as a mask for the password tag */
      --lock-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='5' y='11' width='14' height='9' rx='1.5' fill='black'/%3E%3Cpath d='M8 11 V7.5 a4 4 0 0 1 8 0 V11' fill='none' stroke='black' stroke-width='2'/%3E%3C/svg%3E");
    }

    * { box-sizing: border-box; }
    [hidden] { display: none !important; }

    body {
      margin: 0;
      background: var(--surface);
      color: var(--text);
      font-family: var(--font-body);
      font-weight: 400;
      line-height: 1.6;
      -webkit-font-smoothing: antialiased;
    }

    /* Smooth theme crossfade, applied only AFTER first paint (.theme-ready). */
    .theme-ready * {
      transition: background-color 0.7s ease, color 0.7s ease, border-color 0.7s ease;
    }
    .theme-ready .btn {
      transition: background-color 0.7s ease, color 0.7s ease, border-color 0.7s ease, box-shadow 0.2s var(--ease);
    }

    .no-select { user-select: none; -webkit-user-select: none; }
    h1, h2, h3 { line-height: 1.14; font-weight: 700; }
    a { color: var(--link); text-decoration: none; }

    /* Global keyboard-focus indicator (matches the landing). */
    :focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 2px; }
    .btn:focus-visible { outline-offset: 3px; }

    /* Skip link — first focusable, appears on focus. */
    .skip {
      position: absolute; left: 12px; top: -56px;
      background: var(--surface-raised); color: var(--text);
      padding: 10px 16px; border: 1px solid var(--rule); border-radius: 2px;
      z-index: 100; transition: top 0.18s var(--ease);
    }
    .skip:focus { top: 12px; }

    /* Header: fixed, solid, wordmark left / actions right. */
    .site-header {
      position: fixed; inset: 0 0 auto 0; z-index: 50;
      display: flex; align-items: center; justify-content: space-between;
      gap: 1rem; padding: 0 var(--pad);
      /* Unified header height across landing / dashboard / game (64px) — a fixed
         height + centred contents keeps all three identical despite different
         contents (the profile button's avatar would otherwise make this bar
         taller than the others). */
      height: 64px; box-sizing: border-box;
      background: var(--surface);
      border-bottom: 1px solid var(--border);
    }
    .wordmark {
      font-weight: 800; font-size: 0.95rem; letter-spacing: 0.22em;
      text-transform: uppercase; color: var(--accent);
      display: inline-flex; align-items: center; gap: 0.55rem;
    }
    .wordmark::before {
      content: ""; width: 22px; height: 22px; flex: none;
      background: var(--accent);
      -webkit-mask: var(--d20) center / contain no-repeat;
      mask: var(--d20) center / contain no-repeat;
    }
    .header-actions { display: flex; align-items: center; gap: 0.5rem; }
    #connState { font-size: 0.78rem; color: var(--text-muted); letter-spacing: 0.02em; min-width: 1px; }
    /* Same height as the landing header buttons (44px). .btn already centres its
       content with flex; we just pin the height so the 34px avatar can't stretch
       it, and size the avatar to sit inside. */
    .profile-btn { gap: 0.5rem; height: 44px; padding-top: 0; padding-bottom: 0; }
    .profile-btn .avatar {
      width: 26px; height: 26px; border-radius: 50%; flex: none; object-fit: cover;
      overflow: hidden;   /* clip a framed (scaled) img to the circle */
      border: 1px solid var(--border); background: var(--surface-raised);
    }
    .profile-btn .avatar.mask {
      background: var(--accent);
      -webkit-mask: var(--d20) center / 76% no-repeat; mask: var(--d20) center / 76% no-repeat;
      border: none;
    }
    .profile-btn .pname { font-size: 0.85rem; max-width: 12rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* Buttons: gold-glow hover (verbatim from the landing). */
    .btn {
      font: inherit; font-weight: 600; cursor: pointer;
      border-radius: 2px; padding: 0.6rem 1.2rem;
      min-height: 44px; min-width: 44px;
      display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
      border: 1px solid transparent; background: transparent; color: inherit;
      text-decoration: none; letter-spacing: 0.02em;
      transition: color 0.2s var(--ease), background-color 0.2s var(--ease),
                  box-shadow 0.2s var(--ease), border-color 0.2s var(--ease);
    }
    .btn.secondary { border-color: var(--rule); color: var(--accent); background: transparent; }
    .btn.secondary:hover {
      color: var(--on-accent); background: var(--accent);
      box-shadow: 0 0 6px color-mix(in srgb, var(--accent) 70%, transparent),
                  0 0 18px color-mix(in srgb, var(--accent) 40%, transparent);
    }
    .btn.primary { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
    .btn.primary:hover {
      box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 80%, transparent),
                  0 0 22px color-mix(in srgb, var(--accent) 45%, transparent);
      transform: translateY(-1px);
    }
    .btn.danger { border-color: var(--danger); color: var(--danger); background: transparent; }
    .btn.danger:hover { color: var(--on-accent); background: var(--danger); border-color: var(--danger); }
    .btn.icon { padding: 0.55rem; }
    .btn.icon svg { width: 20px; height: 20px; }
    .btn.small { min-height: 36px; padding: 0.4rem 0.85rem; font-size: 0.85rem; }
    .btn:disabled { opacity: 0.55; cursor: default; box-shadow: none; transform: none; }
    @media (prefers-reduced-motion: reduce) { .btn.primary:hover { transform: none; } }

    /* Main. */
    main { max-width: var(--maxw); margin: 0 auto; padding: 6rem var(--pad) 4rem; }
    .games-head {
      display: flex; align-items: center; justify-content: space-between;
      gap: 1rem; flex-wrap: wrap; margin-bottom: 1.4rem;
    }
    .games-head h1 { font-size: 1.9rem; text-transform: uppercase; letter-spacing: 0.05em; margin: 0; }
    .games-head .actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }

    /* Tabs (APG): one tab stop, roving tabindex, gold underline on the selected. */
    .tabs { display: flex; align-items: center; gap: 0.25rem; border-bottom: 1px solid var(--border); margin-bottom: 0.4rem; flex-wrap: wrap; }
    .tab {
      font: inherit; font-weight: 600; cursor: pointer;
      background: transparent; border: none; color: var(--text-muted);
      padding: 0.6rem 0.9rem; letter-spacing: 0.04em; text-transform: uppercase; font-size: 0.82rem;
      border-bottom: 2px solid transparent; margin-bottom: -1px;
    }
    .tab[aria-selected="true"] { color: var(--accent); border-bottom-color: var(--rule); }
    .tab:hover { color: var(--text); }
    .tabs-list { display: flex; align-items: center; gap: 0.25rem; }
    .tabs-extra { margin-left: auto; display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.82rem; color: var(--text-muted); }
    .tabs-extra input { accent-color: var(--accent); }

    #listStatus { min-height: 1.2em; margin: 0.6rem 0; color: var(--text-muted); font-size: 0.88rem; }

    /* Cards grid. */
    .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); gap: 1rem; margin: 0.6rem 0 1.4rem; align-items: start; }
    .card {
      display: flex; flex-direction: column;
      background: var(--surface-raised); border: 1px solid var(--border); border-radius: 3px;
      overflow: hidden;
      transition: border-color 0.18s var(--ease), box-shadow 0.25s var(--ease);
    }
    /* Hover: the outline goes gold and gains a soft glow, rather than filling. */
    .card:not([data-expanded="true"]):hover {
      border-color: var(--accent);
      box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 55%, transparent),
                  0 0 14px color-mix(in srgb, var(--accent) 35%, transparent);
    }
    @media (prefers-reduced-motion: reduce) { .card { transition: none; } }
    /* When a card is open, its collapsed head stays in the grid as a backdrop
       placeholder (slightly dimmed); the panel itself floats in the overlay. */
    .card[data-expanded="true"] { border-color: var(--accent); }
    .card[data-expanded="true"] .card-head,
    .card[data-expanded="true"] .card-head-actions { opacity: 0.5; }

    /* Collapsed head is the expand trigger (a real button). */
    .card-head {
      display: flex; flex-direction: column; align-items: stretch; gap: 0;
      background: transparent; border: none; padding: 0; margin: 0; cursor: pointer;
      color: inherit; font: inherit; text-align: left; width: 100%;
      position: relative;
    }
    .card-head:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

    .card-cover { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; background: var(--surface); }
    .card-cover.mask { background: var(--surface); position: relative; }
    .card-cover.mask::after {
      content: ""; position: absolute; inset: 0;
      background: var(--border);
      -webkit-mask: var(--d20) center / 34% no-repeat; mask: var(--d20) center / 34% no-repeat;
    }
    .card-head-body { padding: 0.85rem 0.95rem; display: flex; flex-direction: column; gap: 0.4rem; flex: 1; min-width: 0; }
    .card-name { font-size: 1.05rem; font-weight: 700; margin: 0; overflow: hidden; text-overflow: ellipsis; }
    .card-owner { font-size: 0.78rem; color: var(--text-muted); margin: 0; letter-spacing: 0.02em; }
    .badge-row { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; }
    .badge {
      font-size: 0.72rem; letter-spacing: 0.04em; text-transform: uppercase;
      color: var(--text-muted); background: transparent;
      border: 1px solid var(--border); border-radius: 2px; padding: 0.1rem 0.4rem;
      display: inline-flex; align-items: center; gap: 0.25rem;
    }
    .pill-state { font-size: 0.72rem; letter-spacing: 0.04em; text-transform: uppercase; padding: 0.1rem 0.45rem; border: 1px solid var(--border); border-radius: 2px; }
    .pill-state.open { color: var(--accent); border-color: var(--rule); }
    .pill-state.closed { color: var(--text-muted); }
    .card-online { font-size: 0.72rem; color: var(--text-muted); letter-spacing: 0.03em; }

    .card-head-actions { display: flex; align-items: center; gap: 0.5rem; padding: 0 0.95rem 0.85rem; }

    /* ── Floating overlay ─────────────────────────────────────────────────── */
    body.overlay-open { overflow: hidden; }
    .card-overlay { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; }
    .card-overlay[hidden] { display: none; }
    .card-overlay-scrim {
      position: absolute; inset: 0; background: var(--scrim, rgba(0,0,0,0.55));
      -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
      opacity: 0; transition: opacity 0.28s var(--ease);
    }
    .card-overlay.open .card-overlay-scrim { opacity: 1; }
    .card-overlay-stage { position: absolute; inset: 0; pointer-events: none; }

    /* The floating rectangle. JS positions it (FLIP) via inline transform. */
    .card-panel {
      position: fixed; pointer-events: auto;
      display: flex; flex-direction: column; overflow: hidden;
      background: var(--surface-raised); border: 1px solid var(--accent); border-radius: 6px;
      box-shadow: 0 24px 60px rgba(0,0,0,0.5);
      transform-origin: top left;
    }
    /* Animate the FLIP (position/size via transform) + opacity. */
    .card-panel.flipping { transition: transform 0.34s var(--ease), opacity 0.2s var(--ease); }
    @media (prefers-reduced-motion: reduce) {
      .card-panel.flipping { transition: none; }
      .card-overlay-scrim { transition: none; }
    }

    /* Pinned header (banner + title + tabs): does not scroll. */
    .cp-head { flex: none; }
    .card-banner { position: relative; width: 100%; background: var(--surface); border-bottom: 1px solid var(--border); }
    .card-banner-img { display: block; width: 100%; aspect-ratio: 24 / 7; object-fit: cover; background: var(--surface); position: relative; }
    .card-banner-img.mask { aspect-ratio: 24 / 6; }
    .card-banner-img.mask::after {
      content: ""; position: absolute; inset: 0; background: var(--border);
      -webkit-mask: var(--d20) center / 10% no-repeat; mask: var(--d20) center / 10% no-repeat;
    }
    .cp-close {
      position: absolute; top: 0.5rem; right: 0.5rem; width: 2rem; height: 2rem; z-index: 2;
      display: flex; align-items: center; justify-content: center;
      background: rgba(0,0,0,0.45); color: #fff; border: none; border-radius: 50%;
      font-size: 1rem; line-height: 1; cursor: pointer;
    }
    .cp-close:hover { background: rgba(0,0,0,0.65); }
    .cp-close:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
    .cp-head-text { padding: 0.9rem clamp(1rem, 3vw, 1.6rem) 0; }
    .cd-title { font-size: 1.4rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--accent); margin: 0 0 0.15rem; }
    .cd-owner { font-size: 0.82rem; color: var(--text-muted); margin: 0 0 0.7rem; }
    .cp-head-text .cd-tabs { margin-bottom: 0; }

    /* Scrollable body: only this scrolls, contained within the rectangle. */
    .cp-body { flex: 1 1 auto; overflow-y: auto; padding: 1rem clamp(1rem, 3vw, 1.6rem) 1.4rem; }
    .cd-panel { padding-top: 0.4rem; }
    .cd-note { font-size: 0.85rem; color: var(--text-muted); margin: 0 0 0.8rem; }
    .cd-facts { margin: 1rem 0; }
    .cd-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; margin: 0.6rem 0 0.4rem; }
    /* Enter the game is the primary call to action: larger, bolder, and pushed to
       its own prominence so it stands apart from the secondary buttons. */
    /* Header: identity (title + owner) on the left, Enter the game on the right. */
    .cp-head-main { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: 0.7rem; }
    .cp-head-id { min-width: 0; }
    .cp-head-main .cd-owner { margin-bottom: 0; }
    /* The title is the name editor: a pencil appears beside it for the GM. */
    .cd-title-line { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
    .cd-title-line .cd-title { margin: 0; overflow-wrap: anywhere; }
    .cd-title-edit {
      flex: none; width: 1.6rem; height: 1.6rem; padding: 0; border: none; background: none; cursor: pointer;
      border-radius: 4px; display: inline-flex; align-items: center; justify-content: center; opacity: 0.7;
    }
    .cd-title-edit:hover, .cd-title-edit:focus-visible { opacity: 1; outline: none; }
    .cd-title-edit:focus-visible { box-shadow: 0 0 0 2px var(--focus); }
    .cd-title-edit.active { opacity: 1; background: color-mix(in srgb, var(--accent) 18%, transparent); }
    .cd-title-edit-icon {
      width: 1rem; height: 1rem; background: var(--accent);
      -webkit-mask: var(--edit-icon) center / contain no-repeat; mask: var(--edit-icon) center / contain no-repeat;
    }
    .cd-title-input {
      font-size: 1.4rem; font-weight: 700; letter-spacing: 0.02em; flex: 1; min-width: 0; margin: 0;
      padding: 0.1rem 0.5rem; border-radius: 3px; box-sizing: border-box;
      border: 1px solid var(--accent); background: var(--surface); color: var(--text);
    }
    .cd-title-input:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
    /* Enter the game: prominent but sized to sit in harmony with the title. */
    .cp-head-main .cd-enter {
      flex: none; font-size: 0.82rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
      padding: 0.5rem 1.1rem; min-height: 38px; border-radius: 4px; white-space: nowrap;
      box-shadow: 0 0 0 1px var(--accent), 0 4px 12px color-mix(in srgb, var(--accent) 30%, transparent);
    }
    .cp-head-main .cd-enter:hover {
      box-shadow: 0 0 0 1px var(--accent), 0 0 8px var(--accent), 0 6px 16px color-mix(in srgb, var(--accent) 40%, transparent);
    }
    @media (max-width: 520px) {
      .cp-head-main { flex-direction: column; }
      .cp-head-main .cd-enter { width: 100%; text-align: center; }
    }
    .cd-save-hint { font-size: 0.82rem; color: var(--accent); margin: 0.6rem 0 0.2rem; }
    /* Read-only inputs (player view, and GM fields before edit) read as text, not boxes. */
    .cp-body .field input[readonly], .cp-body .field textarea[readonly],
    .cp-body .field select[disabled] {
      background: transparent; border-color: transparent; padding-left: 0; color: var(--text); cursor: default;
    }
    .cp-body .field input:not([readonly]), .cp-body .field textarea:not([readonly]),
    .cp-body .field select:not([disabled]) {
      background: var(--surface); border-color: var(--border);
    }

    #emptyState { padding: 2rem 0; color: var(--text-muted); }

    /* Recently deleted disclosure. */
    .deleted-wrap { margin-top: 1.5rem; border-top: 1px solid var(--border); padding-top: 1rem; }
    .deleted-toggle {
      font: inherit; background: transparent; border: none; color: var(--link); cursor: pointer;
      font-size: 0.88rem; text-transform: uppercase; letter-spacing: 0.04em; padding: 0.2rem 0;
    }
    .deleted-toggle:hover { color: var(--text); }
    #deletedList { margin-top: 0.8rem; display: grid; gap: 0.6rem; }
    .deleted-row {
      display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
      border: 1px solid var(--border); border-radius: 3px; padding: 0.6rem 0.8rem; background: var(--surface-raised);
    }
    .deleted-row .meta { font-size: 0.82rem; color: var(--text-muted); }

    /* Dialogs (verbatim recipes from the landing auth card). */
    dialog {
      border: 1px solid var(--rule); border-radius: 3px; padding: 0;
      background: var(--surface-raised); color: var(--text);
      box-shadow: 0 0 0 1px color-mix(in srgb, var(--rule) 30%, transparent), 0 24px 60px rgba(0,0,0,0.5);
    }
    dialog::backdrop { background: rgba(6, 7, 10, 0.7); backdrop-filter: blur(2px); }
    .dialog-narrow { width: min(29rem, 94vw); }
    .dialog-wide { width: min(46rem, 94vw); }
    .card-inner { padding: 2rem; position: relative; }
    .dialog-wide .card-inner { padding: 0; display: flex; flex-direction: column; max-height: 88vh; }
    .dialog-head {
      position: sticky; top: 0; z-index: 2; background: var(--surface-raised);
      padding: 1.4rem 2rem 0.4rem; border-bottom: 1px solid var(--border);
    }
    .dialog-body { padding: 1.2rem 2rem 2rem; overflow-y: auto; }
    .card-close {
      position: absolute; top: 0.6rem; right: 0.6rem;
      width: 44px; height: 44px; display: grid; place-items: center;
      background: transparent; border: none; color: var(--text-muted);
      font-size: 1.5rem; line-height: 1; cursor: pointer; border-radius: 2px;
      -webkit-appearance: none; appearance: none; -webkit-tap-highlight-color: transparent;
    }
    .card-close:hover { color: var(--accent); }
    .card-inner h2, .dialog-head h2 { font-size: 1.3rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--accent); margin: 0 0 0.3rem; overflow-wrap: anywhere; word-break: break-word; }
    .card-inner h2 + .rule, .dialog-head h2 + .rule { height: 2px; width: 3rem; background: var(--rule); margin-bottom: 1.2rem; }
    .card-inner h3 { font-size: 0.92rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text); margin: 1.2rem 0 0.6rem; }
    .rule { display: block; }

    /* ── Account: landscape ID card ───────────────────────────────────────── */
    .dialog-idcard { width: min(44rem, 95vw); }
    .idcard { position: relative; padding: 2rem 2.2rem; }
    /* Title matches the game panel's .cd-title (1.4rem). */
    .idcard-title { font-size: 1.4rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--accent); margin: 0 0 1.4rem; }
    .idcard-title:focus { outline: none; }
    .idcard-body { display: flex; gap: 2rem; align-items: flex-start; }

    /* Left: framed portrait. Hover reveals an edit-pencil overlay; the whole
       thing is a button that opens the image picker. */
    .idcard-photo { flex: none; width: 10.5rem; display: flex; flex-direction: column; align-items: center; gap: 0.6rem; }
    .idcard-avatar-btn {
      position: relative; width: 10.5rem; height: 10.5rem; padding: 0; border: none; background: none;
      cursor: pointer; border-radius: 8px;
      /* decorative gold frame */
      box-shadow: 0 0 0 2px var(--accent), 0 0 0 4px color-mix(in srgb, var(--accent-strong) 55%, transparent),
                  0 10px 26px rgba(0,0,0,0.42);
    }
    .idcard-avatar-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; }
    .idcard-avatar {
      width: 100%; height: 100%; border-radius: 6px; overflow: hidden;
      background: var(--surface); position: relative; display: block;
    }
    .idcard-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
    .idcard-avatar.mask::after {
      content: ""; position: absolute; inset: 0; background: var(--accent-strong);
      -webkit-mask: var(--d20) center / 58% no-repeat; mask: var(--d20) center / 58% no-repeat;
    }
    /* Edit overlay: a pencil icon (SVG data-URL via mask) instead of a word. */
    .idcard-avatar-overlay {
      position: absolute; inset: 0; border-radius: 6px;
      display: flex; align-items: center; justify-content: center;
      background: rgba(0,0,0,0.5);
      opacity: 0; transition: opacity 0.16s var(--ease);
    }
    .idcard-avatar-overlay::before {
      content: ""; width: 2rem; height: 2rem; background: #fff;
      -webkit-mask: var(--edit-icon) center / contain no-repeat; mask: var(--edit-icon) center / contain no-repeat;
    }
    .idcard-avatar-btn:hover .idcard-avatar-overlay,
    .idcard-avatar-btn:focus-visible .idcard-avatar-overlay { opacity: 1; }
    /* URL field kept as a fallback path; the click opens the picker directly. */
    .idcard-avatar-field { width: 100%; }
    .idcard-avatar-field input {
      width: 100%; font: inherit; padding: 0.65rem 0.75rem; border-radius: 2px;
      border: 1px solid var(--border); background: var(--surface); color: var(--text); min-height: 44px;
    }

    /* Right: details */
    .idcard-details { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.85rem; }
    .idcard-row { display: flex; flex-direction: column; gap: 0.35rem; }
    /* Label matches the game panel's .field label. */
    .idcard-label { font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
    /* One line per value: text (or in-place input) on the left, Change at the end. */
    .idcard-line { display: flex; align-items: center; gap: 0.6rem; }
    /* Values read at the page's base font, like the panel's field content. */
    .idcard-value-text { font: inherit; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
    /* In-place name input matches a standard .field input. */
    .idcard-inline-input {
      font: inherit; flex: 1; min-width: 0; padding: 0.65rem 0.75rem; border-radius: 2px;
      border: 1px solid var(--border); background: var(--surface); color: var(--text); min-height: 44px;
    }
    .idcard-inline-input:focus-visible { border-color: var(--accent); outline: 2px solid var(--focus); outline-offset: 1px; }
    .idcard-change {
      margin-left: auto; flex: none; cursor: pointer;
      background: transparent; border: 1px solid var(--border); border-radius: 3px;
      color: var(--text-muted); font-size: 0.8rem; letter-spacing: 0.03em; text-transform: uppercase;
      padding: 0.4rem 0.85rem; min-height: 36px;
    }
    .idcard-change:hover { color: var(--accent); border-color: var(--rule); }
    .idcard-change:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
    .idcard-change.active { color: var(--accent); border-color: var(--accent); }
    .idcard-dots { letter-spacing: 0.15em; color: var(--text-muted); }
    .idcard-badge {
      flex: none; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
      padding: 0.08rem 0.4rem; border-radius: 2px; border: 1px solid var(--border); color: var(--text-muted);
    }
    .idcard-badge.verified { color: var(--accent); border-color: var(--rule); }
    .idcard-help { font-size: 0.78rem; color: var(--text-muted); }

    /* Revealed email/password inputs — the exact page .field input treatment. */
    .idcard-edit { display: flex; flex-direction: column; gap: 0.5rem; padding: 0.55rem 0 0.2rem; }
    .idcard-edit input {
      width: 100%; font: inherit; padding: 0.65rem 0.75rem; border-radius: 2px;
      border: 1px solid var(--border); background: var(--surface); color: var(--text); min-height: 44px;
    }
    .idcard-edit input:focus-visible { border-color: var(--accent); outline: 2px solid var(--focus); outline-offset: 1px; }
    .idcard-edit .help { font-size: 0.78rem; color: var(--text-muted); }
    /* Delete account, step 2 (#delAccountDialog): same .dialog-narrow box as
       the confirm; dashboard.js gives the card the confirm's exact height, and
       the buttons sit at the bottom. The games still owned (409) each have a
       way to their Manage panel; the list takes the room left and scrolls
       (at least one row stays visible; the card itself scrolls only if a very
       short viewport leaves less than that). */
    .del-card { display: flex; flex-direction: column; box-sizing: border-box; overflow-y: auto; }
    .del-card .del-field { margin-bottom: 0.2rem; }
    .del-owned { list-style: none; margin: 0 0 1rem; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; flex: 0 1 auto; min-height: 2.6rem; overflow-y: auto; }
    .del-owned li { display: flex; align-items: center; gap: 0.6rem; flex: none; }
    .del-owned .del-owned-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .del-actions { display: flex; justify-content: flex-end; margin-top: auto; }
    /* Optional points under a confirm's body (the delete-account warning). */
    .cf-list { margin: -0.6rem 0 1.2rem; padding-left: 1.2rem; display: flex; flex-direction: column; gap: 0.45rem; }

    .idcard-privacy { margin: 0.8rem 0 0; font-size: 0.875rem; color: var(--text-muted); }
    .idcard-privacy a { color: var(--link); text-decoration: underline; text-underline-offset: 2px; }
    .idcard-status { margin: 1rem 0 0; min-height: 1.2rem; font-size: 0.88rem; }
    .idcard-status:empty { margin: 0; min-height: 0; }
    .idcard-actions { display: flex; justify-content: space-between; align-items: center; gap: 0.6rem; margin-top: 1.1rem; }
    /* Right group: Save changes (when shown) just left of the bin. Log out is
       the row's first child, so nothing here can move it. */
    .idcard-actions-end { display: flex; align-items: center; gap: 0.6rem; margin-left: auto; }
    /* Delete account: the app's direct-trash button (.btn.small.danger, as on
       the game page's roster, inventory and image cards): danger border and
       glyph, danger fill on hover. Only the size is set here (36 px square, as
       before), so the bin keeps its place on the row. */
    .btn.idcard-bin { min-height: 36px; min-width: 36px; padding: 0.4rem; }
    .btn.idcard-bin svg { width: 18px; height: 18px; display: block; }

    @media (max-width: 520px) {
      .idcard-body { flex-direction: column; align-items: center; }
      .idcard-details { width: 100%; }
    }

    /* ── Overview (game card), ID-card treatment ───────────────────────────── */

    /* A reusable pencil edit button (same as the header title's), used for the
       description and the joining rows so the whole card shares one affordance. */
    .idcard-edit-btn {
      flex: none; width: 1.6rem; height: 1.6rem; padding: 0; border: none; background: none; cursor: pointer;
      border-radius: 4px; display: inline-flex; align-items: center; justify-content: center; opacity: 0.7;
    }
    .idcard-edit-btn:hover, .idcard-edit-btn:focus-visible { opacity: 1; outline: none; }
    .idcard-edit-btn:focus-visible { box-shadow: 0 0 0 2px var(--focus); }
    .idcard-edit-btn.active { opacity: 1; background: color-mix(in srgb, var(--accent) 18%, transparent); }
    .idcard-edit-icon {
      width: 1rem; height: 1rem; background: var(--accent);
      -webkit-mask: var(--edit-icon) center / contain no-repeat; mask: var(--edit-icon) center / contain no-repeat;
    }

    /* Description: full width, edits in place. Label + pencil on one head row, the
       text (or the textarea that replaces it) below at full width. */
    .ovcard-desc { margin-bottom: 1.4rem; }
    .ovcard-desc-head { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.35rem; }
    .ovcard-desc-head .idcard-edit-btn { margin-left: auto; }
    .ovcard-desc .cd-desc-text {
      margin: 0; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.5;
    }
    .ovcard-desc .cd-desc-text:empty::before { content: "No description"; color: var(--text-muted); }
    .ovcard-desc .cd-f-desc {
      width: 100%; font: inherit; line-height: 1.5; padding: 0.65rem 0.75rem; border-radius: 2px; box-sizing: border-box;
      border: 1px solid var(--border); background: var(--surface); color: var(--text);
      min-height: 5rem; resize: none; overflow: hidden;
    }
    .ovcard-desc .cd-f-desc:focus-visible { border-color: var(--accent); outline: 2px solid var(--focus); outline-offset: 1px; }

    /* Details: joining grouped with the read-only facts as one block. Each line is
       LABEL … value, with joining additionally carrying a pencil + reveal editor. */
    .ov-details { border-top: 1px solid var(--rule); padding-top: 0.9rem; margin-bottom: 1rem; }
    .ov-details-head {
      font-size: 0.76rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em;
      color: var(--text-muted); margin: 0 0 0.6rem;
    }
    .ov-detail { gap: 0.35rem; }
    .ov-detail + .ov-detail-line, .ov-detail-line + .ov-detail-line, .ov-detail-line + .ov-detail { margin-top: 0.55rem; }
    .ov-detail-line { display: flex; align-items: center; gap: 0.8rem; }
    .ov-detail-line .idcard-label { flex: none; min-width: 7.5rem; }
    .ov-detail-line .idcard-value-text { flex: 1; }
    .ov-detail-line .idcard-edit-btn { margin-left: auto; }

    /* The Joining editor's dropdown sits at a natural width, not full-bleed. */
    .cd-vis-edit { margin-top: 0.5rem; }
    .cd-vis-edit .vtt-dd { max-width: 16rem; }
    .cd-vis-edit .cd-pw-field { margin-top: 0.6rem; }

    /* Unsaved-changes bar: sticks to the bottom of the scrolling panel body and
       only shows while dirty, so Save is always reachable but never shifts the
       layout by appearing among the other action buttons. */
    /* Unsaved-changes bar: a flex footer of the card panel, flush at the very
       bottom (outside the scrolling body, nothing beneath it). Shown only when
       dirty, so Save never shifts the layout by appearing among other buttons. */
    .cd-savebar {
      flex: none; margin: 0; padding: 0.8rem clamp(1rem, 3vw, 1.6rem);
      display: flex; align-items: center; justify-content: space-between; gap: 1rem;
      background: var(--surface-raised); border-top: 1px solid var(--accent);
      box-shadow: 0 -6px 18px rgba(0,0,0,0.28);
    }
    .cd-save-hint { font-size: 0.85rem; color: var(--accent); font-weight: 600; }

    /* GM cover editor lives on the big banner: a hover overlay with a pencil. */
    .card-banner-edit {
      position: absolute; inset: 0; padding: 0; border: none; cursor: pointer; z-index: 1;
      background: rgba(0,0,0,0); display: flex; align-items: center; justify-content: center;
      transition: background 0.16s var(--ease);
    }
    .card-banner-edit:hover, .card-banner-edit:focus-visible { background: rgba(0,0,0,0.4); outline: none; }
    .card-banner-edit-icon {
      width: 2.4rem; height: 2.4rem; background: #fff; opacity: 0; transition: opacity 0.16s var(--ease);
      -webkit-mask: var(--edit-icon) center / contain no-repeat; mask: var(--edit-icon) center / contain no-repeat;
    }
    .card-banner-edit:hover .card-banner-edit-icon, .card-banner-edit:focus-visible .card-banner-edit-icon { opacity: 1; }

    @media (max-width: 520px) {
      .ov-detail-line { flex-wrap: wrap; gap: 0.15rem 0.8rem; }
      .ov-detail-line .idcard-label { min-width: 0; }
      .ov-detail-line .idcard-value-text { flex: 1 1 100%; }
      .ov-detail-line .idcard-edit-btn { position: absolute; right: 0; }
      .ov-detail { position: relative; }
    }

    /* ── Find a game ───────────────────────────────────────────────────────── */
    .dialog-find { width: min(46rem, 95vw); }
    .find-inner { position: relative; padding: 2rem 2.2rem; display: flex; flex-direction: column; max-height: 88vh; }
    .find-title { font-size: 1.4rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--accent); margin: 0 0 1.4rem; }
    .find-title:focus { outline: none; }

    /* Search bar: query grows, filter is intrinsic, button aligns to the inputs. */
    .find-controls { display: grid; grid-template-columns: 1fr 15rem auto; gap: 0.6rem; align-items: end; }
    .find-controls .field { margin: 0; }
    /* The Show filter is fixed to the width of its longest option
       ("Password-protected") so the control doesn't resize as the choice changes. */
    .find-vis { width: 15rem; }
    .find-controls label { font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 0.35rem; display: block; }
    .find-controls input, .find-controls select {
      font: inherit; padding: 0.65rem 0.75rem; border-radius: 2px; min-height: 44px;
      border: 1px solid var(--border); background: var(--surface); color: var(--text); box-sizing: border-box; width: 100%;
    }
    .find-controls input:focus-visible, .find-controls select:focus-visible { border-color: var(--accent); outline: 2px solid var(--focus); outline-offset: 1px; }
    .find-go { align-self: end; min-height: 44px; white-space: nowrap; }

    /* Custom themed dropdown (replaces the native <select> so the open option
       list matches the site instead of the OS popup). Same .field footprint. */
    .vtt-dd { position: relative; }
    .vtt-dd-btn {
      font: inherit; text-align: left; cursor: pointer; width: 100%; min-height: 44px;
      padding: 0.65rem 2rem 0.65rem 0.75rem; border-radius: 2px;
      border: 1px solid var(--border); background: var(--surface); color: var(--text);
      display: flex; align-items: center; white-space: nowrap;
    }
    .vtt-dd-btn::after { content: ""; position: absolute; right: 0.7rem; top: 50%; width: 0.5rem; height: 0.5rem;
      border-right: 2px solid var(--text-muted); border-bottom: 2px solid var(--text-muted);
      transform: translateY(-70%) rotate(45deg); pointer-events: none; }
    .vtt-dd-btn:focus-visible { border-color: var(--accent); outline: 2px solid var(--focus); outline-offset: 1px; }
    .vtt-dd[data-open="true"] .vtt-dd-btn { border-color: var(--accent); }
    /* Fixed to the viewport (positioned in JS from the button's rect) so the
       open list escapes the dialog's overflow clipping and floats over the modal. */
    .vtt-dd-list {
      position: fixed; z-index: 200; margin: 0; padding: 0.25rem; list-style: none;
      background: var(--surface-raised); border: 1px solid var(--accent); border-radius: 4px;
      box-shadow: 0 12px 30px rgba(0,0,0,0.45); max-height: 15rem; overflow-y: auto;
    }
    .vtt-dd-list[hidden] { display: none; }
    .vtt-dd-opt {
      padding: 0.55rem 0.7rem; border-radius: 2px; cursor: pointer; font-size: 0.92rem; color: var(--text);
    }
    .vtt-dd-opt:hover, .vtt-dd-opt[data-active="true"] { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); }
    .vtt-dd-opt[aria-selected="true"] { color: var(--accent); font-weight: 600; }

    /* Results: a scroll region of cards echoing the dashboard game cards. */
    .find-results { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 1.1rem;
                    overflow-y: auto; padding-right: 0.2rem; }
    .find-card {
      display: flex; gap: 0.9rem; align-items: center; padding: 0.6rem 0.9rem 0.6rem 0.6rem;
      border: 1px solid var(--border); border-radius: 5px; background: var(--surface); min-height: 4.6rem;
    }
    .find-card:hover { border-color: var(--rule); }
    .find-thumb { flex: none; width: 6.5rem; aspect-ratio: 16 / 10; border-radius: 3px; overflow: hidden;
                  background: var(--surface-raised); position: relative; }
    .find-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
    .find-thumb.mask::after { content: ""; position: absolute; inset: 0; background: var(--border);
      -webkit-mask: var(--d20) center / 44% no-repeat; mask: var(--d20) center / 44% no-repeat; }
    .find-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.2rem; justify-content: center; }
    .find-name { font-size: 1.05rem; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .find-owner { font-size: 0.78rem; color: var(--text-muted); letter-spacing: 0.02em; }
    .find-tags { display: flex; gap: 0.4rem; align-items: center; margin-top: 0.25rem; }
    /* Only password-required games carry a tag; open games show nothing. */
    .find-lock { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
      color: var(--accent); border: 1px solid var(--rule); border-radius: 2px; padding: 0.05rem 0.4rem;
      display: inline-flex; align-items: center; gap: 0.3rem; }
    .find-lock::before { content: ""; width: 0.7rem; height: 0.7rem; background: var(--accent);
      -webkit-mask: var(--lock-icon) center / contain no-repeat; mask: var(--lock-icon) center / contain no-repeat; }
    .find-count { font-size: 0.72rem; color: var(--text-muted); }
    .find-actions { flex: none; display: flex; flex-direction: column; justify-content: center; align-items: flex-end; gap: 0.4rem; }
    .find-actions input { font: inherit; font-size: 0.85rem; padding: 0.4rem 0.5rem; min-height: 36px;
      border: 1px solid var(--border); background: var(--surface-raised); color: var(--text); border-radius: 2px; width: 8rem; }
    /* Non-actionable states shown in place of a Join button. */
    .find-state { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); white-space: nowrap; }
    .find-state.full { color: var(--danger); font-weight: 600; }
    .find-more { margin: 1.4rem auto 1rem; display: block; }
    .find-empty { color: var(--text-muted); font-size: 0.9rem; text-align: center; padding: 1.4rem 0; }

    @media (max-width: 560px) {
      .find-controls { grid-template-columns: 1fr; }
      .find-vis { width: auto; }
      .find-card { flex-wrap: wrap; }
      .find-actions { flex-direction: row; width: 100%; align-items: center; }
    }

    .field { display: grid; gap: 0.35rem; margin-bottom: 1rem; }
    .field label { font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
    .field .help { font-size: 0.78rem; color: var(--text-muted); }
    .field input, .field select, .field textarea {
      font: inherit; padding: 0.65rem 0.75rem; border-radius: 2px;
      border: 1px solid var(--border); background: var(--surface); color: var(--text);
      min-height: 44px; width: 100%;
    }
    .field textarea { min-height: 5rem; resize: vertical; }
    .field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible {
      border-color: var(--accent); outline: 2px solid var(--focus); outline-offset: 1px;
    }
    .field input[aria-invalid="true"] { border-color: var(--danger); }
    .field.with-picker { grid-template-columns: 1fr auto; align-items: end; }
    .field.with-picker label { grid-column: 1 / -1; }
    .field.with-picker .help { grid-column: 1 / -1; }
    .vttpick-open {
      font: inherit; font-size: 0.85rem; min-height: 44px; padding: 0 0.9rem;
      border: 1px solid var(--rule); border-radius: 2px; background: transparent;
      color: var(--accent); cursor: pointer; white-space: nowrap;
    }
    .vttpick-open:hover { color: var(--on-accent); background: var(--accent); }
    .img-preview {
      margin-top: 0.4rem; max-width: 100%; max-height: 8rem; border-radius: 3px;
      border: 1px solid var(--border); object-fit: cover;
    }

    .status { min-height: 1.2em; margin: 0.4rem 0 0.6rem; color: var(--text); font-size: 0.88rem; }
    .field-inline { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
    .danger-block { margin-top: 1.4rem; padding-top: 1rem; border-top: 1px solid var(--border); }
    .danger-block .help { font-size: 0.8rem; color: var(--text-muted); margin: 0.3rem 0 0.7rem; }

    .dialog-body .tabs { margin-top: -0.2rem; }

    /* Member / result rows. */
    .row-list { display: grid; gap: 0.5rem; margin: 0.5rem 0 1rem; }
    .member-row, .result {
      display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
      border: 1px solid var(--border); border-radius: 3px; padding: 0.5rem 0.7rem; background: var(--surface);
    }
    .member-row .who, .result .who { display: flex; align-items: center; gap: 0.5rem; flex: 1; min-width: 8rem; }
    /* The name/meta column must be allowed to shrink (min-width:0) so a long
       username ellipsis-truncates instead of pushing into the action buttons. */
    .member-row .who > :last-child, .result .who > :last-child { min-width: 0; flex: 1; }
    .member-row .avatar, .result .avatar {
      width: 44px; height: 44px; border-radius: 50%; flex: none; object-fit: cover;
      overflow: hidden;   /* clip a framed (scaled) img to the circle */
      border: 1px solid var(--border); background: var(--surface-raised);
    }
    .member-row .name, .result .name { font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .member-row .meta, .result .meta { font-size: 0.78rem; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .member-row .row-actions, .result .row-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; }
    /* Richer search result: thumbnail + stacked name/owner/description. */
    .result-rich { align-items: flex-start; }
    .result-rich .who { flex-direction: column; align-items: stretch; gap: 0.15rem; }
    .result-thumb {
      width: 84px; height: 52px; flex: none; border-radius: 3px; overflow: hidden;
      border: 1px solid var(--border); background: var(--surface-raised); position: relative;
    }
    .result-thumb.mask::after {
      content: ""; position: absolute; inset: 0; background: var(--border);
      -webkit-mask: var(--d20) center / 42% no-repeat; mask: var(--d20) center / 42% no-repeat;
    }
    .result-desc {
      font-size: 0.82rem; color: var(--text); margin: 0.1rem 0 0;
      display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    }
    .result-rich .row-actions { align-items: flex-start; }
    .pw-wrap { position: relative; display: flex; flex: 1; min-width: 10rem; }
    .pw-wrap input { flex: 1; padding-right: 4rem; }
    .pw-toggle {
      position: absolute; right: 0.3rem; top: 50%; transform: translateY(-50%);
      min-height: 38px; padding: 0 0.6rem; font: inherit; font-size: 0.78rem;
      background: transparent; border: none; color: var(--link); cursor: pointer;
    }
    .invite-row { display: flex; gap: 0.5rem; align-items: center; margin: 0.4rem 0 0.8rem; }
    .invite-row input { flex: 1; font: inherit; padding: 0.55rem 0.7rem; border: 1px solid var(--border); border-radius: 2px; background: var(--surface); color: var(--text); }
    .kv { display: grid; grid-template-columns: auto 1fr; gap: 0.3rem 0.8rem; margin: 0.6rem 0; font-size: 0.9rem; }
    .kv dt { color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.76rem; align-self: center; }
    .kv dd { margin: 0; }

    /* Mobile. */
    @media (max-width: 640px) {
      .wordmark { font-size: 0.8rem; letter-spacing: 0.15em; }
      .profile-btn .pname { display: none; }
      .cards { grid-template-columns: 1fr; }
      .card-inner, .dialog-body { padding-left: 1.2rem; padding-right: 1.2rem; }
      .dialog-head { padding-left: 1.2rem; padding-right: 1.2rem; }
      .tabs { overflow-x: auto; flex-wrap: nowrap; }
    }
    @media (prefers-reduced-motion: reduce) {
      .skip { transition: none; }
    }
