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

        /* ---- skin system: a handful of CSS custom properties every skin sets, consumed by the
           fixed background layers below, .site-nav's glass treatment, AND .panel's glass treatment
           (--skin-content-*). Nav and content panels use separate bg/blur/border/text sets --
           content panels need to stay more opaque than the nav (dense card grids/forms need more
           contrast than a simple icon+text list) even within the same skin. --skin-bg-alpha /
           --skin-overlay-color are the two knobs the Skin Tuner (see applySkinTheme() /
           openSkinTuner()) exposes for live fine-tuning; the rest are per-skin "structural" values
           baked into SKIN_THEMES. Note: only .panel's own background/border/base text color follow
           the skin -- specific hardcoded accent colors on individual elements inside it (tab
           labels, stat boxes, etc.) are unchanged, so contrast on dark skins is decent but not
           pixel-tuned everywhere yet. */
        :root {
            --card-scale: 1;
            --skin-bg-alpha: 1;
            --skin-overlay-color: rgba(255,255,255,0.25);
            --skin-backdrop: #dfe7f6;
            --skin-panel-bg: rgba(255,255,255,0.5);
            --skin-panel-blur: 18px;
            --skin-panel-border: rgba(255,255,255,0.65);
            --skin-panel-shadow: rgba(20,40,70,0.16);
            --skin-text: #1c3d63;
            --skin-muted: #4a6a94;
            --skin-link: #2c5183;
            --skin-active-1: #7fb8ed;
            --skin-active-2: #4a90e2;
            --skin-active-text: #ffffff;
            --skin-donate: #c1416a;
            --skin-content-bg: rgba(255,255,255,0.82);
            --skin-content-blur: 14px;
            --skin-content-border: rgba(255,255,255,0.7);
            --skin-content-shadow: rgba(20,40,70,0.14);
            --skin-content-text: #2c3e50;
            --skin-accent-text: #4a90e2;
            /* Derived, not skin-tuner-exposed -- always recomputed from the vars above, so
               every 'muted text'/'nested card'/'divider' surface in the app automatically
               follows whichever skin is active without needing its own per-skin hardcoded
               value. See CLAUDE.md "Skin System" for why this replaced dozens of flat grays
               (#666/#999/#f5f9ff/etc.) that used to assume a plain white page. */
            --skin-card-bg: color-mix(in srgb, var(--skin-content-text) 7%, transparent);
            --skin-card-bg-hover: color-mix(in srgb, var(--skin-active-2) 12%, var(--skin-card-bg));
            --skin-card-bg-active: color-mix(in srgb, var(--skin-active-2) 16%, var(--skin-card-bg));
            --skin-card-border: color-mix(in srgb, var(--skin-content-text) 14%, transparent);
            --skin-card-border-active: color-mix(in srgb, var(--skin-active-2) 55%, transparent);
            --skin-text-muted: color-mix(in srgb, var(--skin-content-text) 68%, transparent);
            --skin-text-faint: color-mix(in srgb, var(--skin-content-text) 42%, transparent);
            --skin-divider: color-mix(in srgb, var(--skin-content-text) 16%, transparent);
            --skin-accent-gradient: linear-gradient(135deg, var(--skin-accent-text), color-mix(in srgb, var(--skin-accent-text) 65%, black));
            /* The dark scrim behind text-over-art (card names, class names in the mosaic, boss
               stat bars) -- mostly black (so it stays dark enough to keep white text legible over
               ANY card art/portrait, not just the ones the picked accent happens to suit) with a
               quarter of the current skin's accent mixed in, so the scrim itself reads as
               "belonging" to the active skin instead of being one fixed neutral black regardless
               of which skin is active. */
            --skin-fade-color: color-mix(in srgb, var(--skin-accent-text) 25%, black);
            /* Background PHOTO vars -- kept outside the --skin-* prefix above only for naming
               clarity; each skin now owns its own photo/zoom/position (see "SKIN SYSTEM" in JS --
               "cada fundo vai ser uma skin") and applySkinVars() sets all of these directly from
               the active skin, same as every --skin-* var. This :root default is just the
               pre-JS-load fallback (matches the default skin's own photo). */
            --bg-image: url('../backgrounds/landscape.jpg');
            --bg-size: cover;
            --bg-pos-x: 50%;
            --bg-pos-y: 42%;
        }

        /* Fixed full-viewport backdrop: the landscape image (opacity = --skin-bg-alpha) plus a
           tint layer (--skin-overlay-color, hue+strength both skin-tunable) on top of it. Uses
           position:fixed + a negative z-index instead of body{background-attachment:fixed},
           which has known rendering bugs on mobile Safari -- this app's audience is mobile-first
           (see CLAUDE.md "Visual Identity"). Both sit behind every real element without needing
           z-index on anything else, since fixed+negative-z-index places them behind the root
           stacking context's normal content regardless of DOM order. */
        #bgImageLayer {
            position: fixed;
            inset: 0;
            z-index: -2;
            background-image: var(--bg-image);
            /* --bg-size is an explicit "<width>px <height>px" computed in JS (applyBgSize()), not
               the "cover" keyword -- a CSS-transform zoom (tried first) only magnifies whatever
               "cover" already rendered AFTER layout, without giving background-position any more
               room to pan (cover's own fit/slack is computed against the UNSCALED element, so a
               transform zoom and a position pan are fully decoupled from each other -- confirmed
               live: at 156% zoom the horizontal-position slider visibly did nothing). Sizing the
               background explicitly to (cover-fit px) * zoom instead means MORE of the image is
               actually being laid out oversized within the container, which is what makes
               background-position's percentage math (X% of the overflow) have real room to move
               as zoom increases -- panning now genuinely reveals a different part of the photo,
               and that range grows with zoom, matching what "zoom + position" implies. */
            background-size: var(--bg-size);
            background-position: var(--bg-pos-x) var(--bg-pos-y);
            opacity: var(--skin-bg-alpha);
            pointer-events: none;
        }
        #bgOverlayLayer {
            position: fixed;
            inset: 0;
            z-index: -1;
            background: var(--skin-overlay-color);
            pointer-events: none;
        }

        html {
            /* Reserve scrollbar space always, so pages with/without scroll don't shift
               the whole layout (and the fixed background) sideways when switching tabs. */
            scrollbar-gutter: stable;
            overflow-y: scroll;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: var(--skin-backdrop);
            min-height: 100vh;
            display: flex;
        }

        .container { max-width: 1800px; margin: 0 auto; }
