@import url('https://fonts.googleapis.com/css2?family=Silkscreen:wght@400;700&display=swap');

/* ===== assets/css/theme-tokens.css ===== */
/**
 * THEME TOKENS - Single source of truth for the visual language
 * =============================================================
 *
 * Everything the site looks like is decided here: palette, surfaces, glass,
 * borders, radii, spacing, type and motion. Component stylesheets consume these
 * and should not invent their own values.
 *
 * WHY THIS FILE EXISTS
 * --------------------
 * The theme had drifted into nine near-identical mauves, ten panel backgrounds,
 * five backdrop-filter recipes and six border radii, so no two panels on a page
 * looked like they belonged to the same design. Sixteen hundred !important
 * declarations across forty-six stylesheets were the symptom, not the cause.
 *
 * NAMING
 * ------
 * The `--re-` prefix was already established by theme-color-fix.css; this file
 * keeps it and extends it rather than introducing a competing scheme.
 *
 * ONE IMPORTANT NOTE ON CONTRAST
 * ------------------------------
 * The page background is a dark purple gradient in BOTH themes - the light
 * theme changes panels and accents, not the ground. Foreground colours are
 * therefore light in both themes. This is deliberate; it is what makes the
 * reactive particle background readable.
 *
 * Load order: immediately after Bootstrap, before every component stylesheet.
 */

:root {
    /* ============================================================
       BRAND RAMP
       One ordered scale replacing #826b7d / #5d4b60 / #9b7b8e /
       #6e5771 / #4a3a4d / #c9a0dc / #ba9ebb / #a088a0 / #9d8098 /
       #e5dbe5, which were being used interchangeably.
       ============================================================ */
    --re-brand-900: #221a27;
    --re-brand-800: #2f2434;
    --re-brand-700: #4a3a4d;
    --re-brand-600: #5d4b60;   /* primary fills, buttons */
    --re-brand-500: #6e5771;   /* primary hover */
    --re-brand-400: #826b7d;   /* the signature mauve - accents, icons, rules */
    --re-brand-300: #9d8098;
    --re-brand-200: #ba9ebb;   /* bright accent on dark ground */
    --re-brand-100: #d8c8d6;
    --re-brand-050: #ede6ec;

    /* Raw channels, for building rgba() without hardcoding the hex again. */
    --re-brand-400-rgb: 130, 107, 125;
    --re-brand-600-rgb: 93, 75, 96;
    --re-ink-rgb: 20, 15, 22;

    /* ============================================================
       TEXT
       --bs-body-bg used to be used as a text colour in 567 places.
       It is the PAGE BACKGROUND variable, so it inverted with the
       theme and made half the site invisible in dark mode. These
       are the replacements.
       ============================================================ */
    --re-text-color:   rgba(245, 240, 244, 0.92);
    --re-text-muted:   rgba(245, 240, 244, 0.66);
    --re-text-subtle:  rgba(245, 240, 244, 0.46);
    --re-heading-color: #ffffff;
    --re-accent-color: var(--re-brand-200);

    --re-admin-text:       var(--re-text-color);
    --re-admin-text-muted: var(--re-text-muted);

    /* ============================================================
       SURFACES
       Three steps, not ten. Panels sit on the reactive background,
       so they are tinted darkness rather than opaque boxes.
       ============================================================ */
    --re-surface-sunken: rgba(8, 5, 10, 0.50);                    /* inputs, wells, tags   */
    /*
     * The default panel, and the one token that was still plum.
     *
     * --re-ink-rgb is a fixed near-black purple from before palettes existed,
     * so every panel on the site - the navbar, the header strip, cards, the
     * glass everywhere - kept a warm cast no matter which palette was on. It
     * is subtle at 55% opacity and invisible under Mauve, which is exactly
     * why it survived: it only shows up once the site can be grey or green.
     *
     * Derived from the palette's darkest step instead, same as the ground
     * gradient and the player bar, so all three agree. rgba first as the
     * fallback for browsers without color-mix.
     */
    --re-surface:        rgba(var(--re-ink-rgb), 0.55);           /* the default panel     */
    --re-surface:        color-mix(in srgb, var(--re-brand-900) 55%, transparent);
    /*
     * Declared twice on purpose. The rgba() line is the fallback; the
     * color-mix() line says the same thing but derives from --re-brand-400
     * itself rather than from a list of raw channels, which is what lets it
     * CROSSFADE when a palette changes. A registered custom property can be
     * interpolated; "130, 107, 125" cannot. Browsers without color-mix keep
     * the first declaration and simply snap instead.
     * See assets/css/theme-palettes.css.
     */
    --re-surface-raised: rgba(var(--re-brand-400-rgb), 0.10);     /* alternate / accented  */
    --re-surface-raised: color-mix(in srgb, var(--re-brand-400) 10%, transparent);
    --re-surface-hover:  rgba(var(--re-brand-400-rgb), 0.28);
    --re-surface-hover:  color-mix(in srgb, var(--re-brand-400) 28%, transparent);
    --re-surface-active: rgba(var(--re-brand-400-rgb), 0.38);
    --re-surface-active: color-mix(in srgb, var(--re-brand-400) 38%, transparent);

    /* ============================================================
       GLASS
       Two recipes. Previously there were five, plus eight
       components with no backdrop-filter at all sitting in the
       middle of a glass design.
       ============================================================ */
    --re-glass:        blur(16px) saturate(180%) contrast(110%);
    --re-glass-subtle: blur(10px) saturate(150%);
    --re-noise-opacity: 0.06;

    /* ============================================================
       BORDERS & EDGES
       ============================================================ */
    /* Same pairing as the surfaces above, for the same reason. */
    --re-border:        rgba(var(--re-brand-400-rgb), 0.28);
    --re-border:        color-mix(in srgb, var(--re-brand-400) 28%, transparent);
    --re-border-strong: rgba(var(--re-brand-400-rgb), 0.50);
    --re-border-strong: color-mix(in srgb, var(--re-brand-400) 50%, transparent);
    --re-border-faint:  rgba(var(--re-brand-400-rgb), 0.16);
    --re-border-faint:  color-mix(in srgb, var(--re-brand-400) 16%, transparent);
    --re-rim:           inset 0 1px 0 0 rgba(255, 255, 255, 0.14);

    /* ============================================================
       ELEVATION
       ============================================================ */
    --re-shadow-1: 0 4px 16px rgba(0, 0, 0, 0.35), var(--re-rim);
    --re-shadow-2: 0 10px 32px rgba(0, 0, 0, 0.45), var(--re-rim);
    --re-shadow-hover: 0 14px 40px rgba(0, 0, 0, 0.50),
                       inset 0 1px 0 0 rgba(255, 255, 255, 0.22);

    /* ============================================================
       RADII

       One scale, five steps. Everything else in the codebase - 0.25rem, 1px,
       2px, 3px, 4px, 5px, 8px, 12px, 15%, 16px, 20px, 24px, 40px - has been
       mapped onto it. 50% is kept only where a shape is genuinely circular
       (avatars, icon buttons); vendor stylesheets are left alone.
       ============================================================ */
    --re-radius-xs: 3px;    /* hairline: progress tracks, meters, rules */
    --re-radius-sm: 6px;    /* tags, chips, small controls  */
    --re-radius-md: 10px;   /* buttons, inputs, chrome bars */
    --re-radius-lg: 14px;   /* panels, cards, containers    */
    --re-radius-pill: 999px;

    /* ============================================================
       SPACING - a 4px rhythm
       ============================================================ */
    --re-space-1: 4px;
    --re-space-2: 8px;
    --re-space-3: 12px;
    --re-space-4: 16px;
    --re-space-5: 24px;
    --re-space-6: 32px;
    --re-space-7: 48px;

    /* ============================================================
       TYPE
       Three faces, three jobs:
         display - ThaleahFat, the retro pixel identity. Headings.
         ui      - Silkscreen, 8px uppercase. Chrome, labels, meta.
         body    - Albert Sans. Everything you actually read.
       Albert Sans was named in hundreds of rules but never loaded;
       templates/header.php now pulls it from Google Fonts.
       ============================================================ */
    --re-font-display: 'ThaleahFat', 'Albert Sans', 'Noto Sans JP', sans-serif;
    --re-font-ui:      'Silkscreen', 'Courier New', monospace;
    --re-font-body:    'Albert Sans', 'Noto Sans JP', system-ui, sans-serif;

    --re-size-ui:   8px;      /* the Silkscreen chrome size, site-wide */
    --re-size-xs:   0.78rem;
    --re-size-sm:   0.875rem;
    --re-size-base: 1rem;
    --re-size-lg:   1.25rem;
    --re-size-xl:   1.75rem;
    --re-size-2xl:  2.25rem;
    --re-size-3xl:  3rem;

    --re-tracking-ui: 1px;
    --re-leading-body: 1.7;

    /* ============================================================
       MOTION
       ============================================================ */
    --re-transition: 0.25s ease;
    --re-transition-slow: 0.4s ease;
    --re-lift: translateY(-2px);

    /* ============================================================
       GROUND
       The static gradient the reactive particle layer sits on.
       Kept dark in both themes so the particles stay visible; the
       light theme lifts it rather than inverting it.
       ============================================================ */
    /* ============================================================
       HAIRLINE
       The rules and dividers: section heads, list rows, card edges,
       the footer columns. It lived on `.home` and everything else
       used the literal fallback, which is why every line on every
       other page stayed plum while the palette changed around it.
       ============================================================ */
    --home-rule: rgba(130, 107, 125, 0.22);
    --home-rule: color-mix(in srgb, var(--re-brand-400) 22%, transparent);
    --page-rule: var(--home-rule);

    --re-ground-inner: var(--re-brand-700);
    --re-ground-outer: var(--re-brand-900);
}

/* ================================================================
   LIGHT THEME

   The ground stays dark (see the note at the top), so only the
   panel tints, accent and edges shift - panels lighten and warm
   slightly so the light theme reads as a different mood rather
   than a different site.
   ================================================================ */

[data-bs-theme="light"] {
    /* A lifted, misty version of the same design - frosted white glass on a
       lighter plum ground - rather than a second, unrelated look. The previous
       light theme turned some panels white and left others dark, which is what
       made the two modes feel like different sites. */
    --re-ground-inner: var(--re-brand-500);
    --re-ground-outer: var(--re-brand-800);

    --re-surface-sunken: rgba(34, 26, 39, 0.32);
    --re-surface:        rgba(255, 255, 255, 0.13);
    --re-surface-raised: rgba(255, 255, 255, 0.22);
    --re-surface-hover:  rgba(255, 255, 255, 0.26);
    --re-surface-active: rgba(255, 255, 255, 0.34);

    --re-border:        rgba(255, 255, 255, 0.30);
    --re-border-strong: rgba(255, 255, 255, 0.50);
    --re-border-faint:  rgba(255, 255, 255, 0.18);

    --re-shadow-1: 0 4px 16px rgba(34, 26, 39, 0.28),
                   inset 0 1px 0 0 rgba(255, 255, 255, 0.34);
    --re-shadow-2: 0 10px 32px rgba(34, 26, 39, 0.34),
                   inset 0 1px 0 0 rgba(255, 255, 255, 0.34);

    --re-accent-color: var(--re-brand-050);
}

/* ================================================================
   BOOTSTRAP BRIDGE

   Bootstrap components read --bs-* variables. Point the ones that
   matter at the brand ramp so buttons, links, badges and focus
   rings inherit the theme instead of shipping Bootstrap blue and
   needing a 400-line override file to undo it.

   --bs-body-bg is deliberately NOT redefined here: it is a real
   background variable that Bootstrap uses for cards, modals,
   dropdowns and form controls. The 567 places that misused it as a
   text colour were corrected at the source instead.
   ================================================================ */

:root,
[data-bs-theme="dark"],
[data-bs-theme="light"] {
    --bs-primary: var(--re-brand-600);
    --bs-primary-rgb: var(--re-brand-600-rgb);
    --bs-secondary: var(--re-brand-400);

    --bs-link-color: var(--re-brand-400);
    --bs-link-hover-color: var(--re-brand-200);
    --bs-link-color-rgb: var(--re-brand-400-rgb);

    --bs-border-color: var(--re-border);
    --bs-emphasis-color: var(--re-heading-color);
    --bs-secondary-color: var(--re-text-muted);
    --bs-heading-color: var(--re-heading-color);

    --bs-focus-ring-color: rgba(var(--re-brand-400-rgb), 0.45);
    --bs-focus-ring-width: 2px;
}

/* ================================================================
   SHARED PRIMITIVES

   The glass panel treatment is one recipe applied through a single
   class so panels cannot drift apart again. Component stylesheets
   compose with this rather than re-declaring blur + noise + rim.
   ================================================================ */

.re-panel,
.re-panel-raised,
.re-panel-sunken {
    position: relative;
    border-radius: var(--re-radius-lg);
    border: 1px solid var(--re-border);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    box-shadow: var(--re-shadow-1);
    color: var(--re-text-color);
}

.re-panel        { background: var(--re-surface); }
.re-panel-raised { background: var(--re-surface-raised); box-shadow: var(--re-shadow-2); }
.re-panel-sunken { background: var(--re-surface-sunken); box-shadow: none; }

/* Fine noise, so glass reads as a material rather than a flat tint. */
.re-noise::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    z-index: 0;
    opacity: var(--re-noise-opacity);
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='reNoise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23reNoise)'/%3E%3C/svg%3E");
}

/* Content has to clear the noise layer. */
.re-noise > * {
    position: relative;
    z-index: 1;
}

/* One focus treatment for the whole site. */
.re-focusable:focus-visible,
a:focus-visible,
button:focus-visible,
[role="button"]:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    outline: 2px solid var(--re-brand-200);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    :root {
        --re-transition: 0s;
        --re-transition-slow: 0s;
        --re-lift: none;
    }
}


/* ===== assets/css/bootstrap-blue-fixes.css ===== */
/**
 * Bootstrap Blue Color Fixes
 * Replace all Bootstrap blue (#007bff) with theme purple
 * Theme colors: var(--re-brand-400) (primary), var(--re-brand-600) (dark)
 */

/* ============================================
   OVERRIDE BOOTSTRAP PRIMARY COLOR
   ============================================ */

:root,
[data-bs-theme="light"],
[data-bs-theme="dark"] {
  /* Replace Bootstrap blue with theme purple */
  --bs-primary: var(--re-brand-400);
  --bs-primary-rgb: 130, 107, 125;
  --bs-primary-text: var(--re-brand-400);
  --bs-primary-bg: rgba(130, 107, 125, 0.1);
  --bs-primary-border: rgba(130, 107, 125, 0.3);
  
  /* Link colors */
  --bs-link-color: var(--re-brand-400);
  --bs-link-color-rgb: 130, 107, 125;
  --bs-link-hover-color: var(--re-brand-600);
  --bs-link-hover-color-rgb: 93, 75, 96;
  
  /* Form controls */
  --bs-form-valid-color: #28a745;
  --bs-form-valid-border-color: #28a745;
  --bs-form-invalid-color: #dc3545;
  --bs-form-invalid-border-color: #dc3545;
  
  /* Focus state */
  --bs-focus-ring-color: rgba(130, 107, 125, 0.25);
  
  /* Button colors */
  --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(130, 107, 125, 0.5);
}

/* ============================================
   TEXT COLORS
   ============================================ */

.text-primary {
  color: var(--re-brand-400);
}

.text-primary-emphasis {
  color: var(--re-brand-600);
}

a {
  color: var(--re-brand-400);
}

a:hover {
  color: var(--re-brand-600);
}

/* ============================================
   BACKGROUND COLORS
   ============================================ */

.bg-primary {
  background-color: var(--re-brand-400);
}

.bg-primary-subtle {
  background-color: rgba(130, 107, 125, 0.1);
}

/* ============================================
   BORDER COLORS
   ============================================ */

.border-primary {
  border-color: var(--re-brand-400);
}

.border-primary-subtle {
  border-color: rgba(130, 107, 125, 0.3);
}

/* ============================================
   BUTTONS
   ============================================ */

.btn-primary {
  background-color: var(--re-brand-400);
  border-color: var(--re-brand-600);
  color: white;
}

.btn-primary:hover {
  background-color: var(--re-brand-600);
  border-color: var(--re-brand-700);
}

.btn-primary:focus {
  background-color: var(--re-brand-600);
  border-color: var(--re-brand-700);
  box-shadow: 0 0 0 0.25rem rgba(130, 107, 125, 0.5);
}

.btn-primary:active,
.btn-primary.active {
  background-color: var(--re-brand-700);
  border-color: #3d2f3e;
}

.btn-outline-primary {
  color: var(--re-brand-400);
  border-color: var(--re-brand-400);
}

.btn-outline-primary:hover {
  background-color: var(--re-brand-400);
  border-color: var(--re-brand-400);
  color: white;
}

.btn-link {
  color: var(--re-brand-400);
}

.btn-link:hover {
  color: var(--re-brand-600);
}

/* ============================================
   FORM CONTROLS
   ============================================ */

.form-control:focus {
  border-color: var(--re-brand-400);
  box-shadow: 0 0 0 0.25rem rgba(130, 107, 125, 0.25);
}

.form-select:focus {
  border-color: var(--re-brand-400);
  box-shadow: 0 0 0 0.25rem rgba(130, 107, 125, 0.25);
}

.form-check-input:checked {
  background-color: var(--re-brand-400);
  border-color: var(--re-brand-400);
}

.form-check-input:focus {
  border-color: var(--re-brand-400);
  box-shadow: 0 0 0 0.25rem rgba(130, 107, 125, 0.25);
}

/* ============================================
   ALERTS
   ============================================ */

.alert-primary {
  color: var(--re-brand-600);
  background-color: rgba(130, 107, 125, 0.1);
  border-color: rgba(130, 107, 125, 0.3);
}

/* ============================================
   BADGES
   ============================================ */

.badge.bg-primary,
.badge.text-bg-primary {
  background-color: var(--re-brand-400);
  color: white;
}

/* ============================================
   PAGINATION
   ============================================ */

.pagination .page-link {
  color: var(--re-brand-400);
}

.pagination .page-link:hover {
  color: var(--re-brand-600);
  background-color: rgba(130, 107, 125, 0.1);
  border-color: rgba(130, 107, 125, 0.3);
}

.pagination .page-item.active .page-link {
  background-color: var(--re-brand-400);
  border-color: var(--re-brand-400);
}

/* ============================================
   PROGRESS BARS
   ============================================ */

.progress-bar {
  background-color: var(--re-brand-400);
}

.progress-bar-striped {
  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}

/* ============================================
   NAVS & TABS
   ============================================ */

.nav-link {
  color: var(--re-brand-400);
}

.nav-link:hover,
.nav-link:focus {
  color: var(--re-brand-600);
}

.nav-pills .nav-link.active {
  background-color: var(--re-brand-400);
}

.nav-tabs .nav-link.active {
  color: var(--re-brand-400);
  border-color: #dee2e6 #dee2e6 #fff;
}

/* ============================================
   LIST GROUPS
   ============================================ */

.list-group-item-primary {
  color: var(--re-brand-600);
  background-color: rgba(130, 107, 125, 0.1);
}

.list-group-item-primary.list-group-item-action:hover {
  color: var(--re-brand-600);
  background-color: rgba(130, 107, 125, 0.2);
}

.list-group-item-primary.list-group-item-action.active {
  color: #fff;
  background-color: var(--re-brand-400);
  border-color: var(--re-brand-400);
}

/* ============================================
   DROPDOWNS
   ============================================ */

.dropdown-item.active,
.dropdown-item:active {
  background-color: var(--re-brand-400);
}

/* ============================================
   MUSIC PLAYER SPECIFIC FIXES
   ============================================ */

#persistent-music-player .btn-primary {
  background-color: var(--re-brand-400);
  border-color: var(--re-brand-600);
}

#persistent-music-player .progress-bar {
  background-color: var(--re-brand-400);
}

/* ============================================
   ADMIN DASHBOARD FIXES
   ============================================ */

/* Server stats text colors */
.server-info-footer,
.server-info-footer * {
  color: var(--re-text-color, rgba(255, 255, 255, 0.92));
}

/* Analytics table text */
#map-stats,
#map-stats *,
.analytics-table,
.analytics-table td {
  color: var(--re-text-color, rgba(255, 255, 255, 0.92));
}

/* Top locations list */
.top-locations,
.top-locations * {
  color: var(--re-text-color, rgba(255, 255, 255, 0.92));
}

/* Admin stat cards - ensure readable text */
.col-md-2 > div[style*="background"],
.col-md-3 > div[style*="background"] {
  color: var(--re-text-color, rgba(255, 255, 255, 0.92));
}

/* Chart labels and legends */
.chart-legend,
.chart-label {
  color: var(--re-text-color, rgba(255, 255, 255, 0.92));
}

/* ============================================
   TABLES
   ============================================ */

.table-primary {
  --bs-table-bg: rgba(130, 107, 125, 0.1);
  --bs-table-striped-bg: rgba(130, 107, 125, 0.05);
  --bs-table-hover-bg: rgba(130, 107, 125, 0.15);
  --bs-table-active-bg: rgba(130, 107, 125, 0.2);
  --bs-table-border-color: rgba(130, 107, 125, 0.2);
}

/* ============================================
   MODALS
   ============================================ */

.modal-header {
  border-bottom-color: rgba(130, 107, 125, 0.2);
}

.modal-footer {
  border-top-color: rgba(130, 107, 125, 0.2);
}

/* ============================================
   POPOVERS & TOOLTIPS
   ============================================ */

.popover {
  border-color: rgba(130, 107, 125, 0.3);
}

.popover-header {
  background-color: rgba(130, 107, 125, 0.1);
  border-bottom-color: rgba(130, 107, 125, 0.2);
}

.tooltip {
  color: var(--re-text-color, rgba(255, 255, 255, 0.92));
}

/* ============================================
   SPECIFIC COMPONENT FIXES
   ============================================ */

/* Bootstrap icons in buttons */
.btn i.bi,
.btn svg {
  color: inherit;
}

/* Card links */
.card-link {
  color: var(--re-brand-400);
}

.card-link:hover {
  color: var(--re-brand-600);
}

/* Accordion */
.accordion-button:not(.collapsed) {
  color: var(--re-brand-600);
  background-color: rgba(130, 107, 125, 0.1);
}

.accordion-button:focus {
  border-color: var(--re-brand-400);
  box-shadow: 0 0 0 0.25rem rgba(130, 107, 125, 0.25);
}

/* Offcanvas */
.offcanvas-header {
  border-bottom-color: rgba(130, 107, 125, 0.2);
}

/* Close buttons */
.btn-close:focus {
  box-shadow: 0 0 0 0.25rem rgba(130, 107, 125, 0.25);
}

/* ============================================
   TEXT IN CONTAINERS WITH BACKGROUNDS
   ============================================ */

/* Ensure text is visible in dark containers */
[style*="background: rgba(0,0,0"] *:not(.text-primary):not(.text-danger):not(.text-warning):not(.text-success):not(.text-info),
[style*="background:rgba(0,0,0"] *:not(.text-primary):not(.text-danger):not(.text-warning):not(.text-success):not(.text-info) {
  color: var(--re-text-color, rgba(255, 255, 255, 0.92));
}

/* Server footer all elements */
footer[style*="position: fixed"] *,
.server-info-footer * {
  color: var(--re-text-color, rgba(255, 255, 255, 0.92));
}


/* ===== assets/css/bs-theme-overrides.css ===== */
body {
  /*
   * The static ground the particle network and film grain sit on top of.
   * (The reactive layers themselves - #particle-network and .bg - are not
   * touched by this stylesheet.)
   *
   * The outer stop used to be var(--bs-body-color), which is Bootstrap's
   * TEXT colour: #adb5bd, a light grey. So the page faded from plum to pale
   * grey at the edges and washed every glass panel out into the same haze -
   * the mirror image of the --bs-body-bg-as-text-colour bug. Both stops now
   * come from the brand ramp, which is what lets the panels read as glass.
   *
   * To restore the previous look, put --bs-link-hover-color and
   * --bs-body-color back as the two stops.
   */
  background: radial-gradient(var(--re-ground-inner) 0%, var(--re-ground-outer) 100%);
  background-attachment: fixed;
  background-size: cover;
  min-height: 100vh;
  font-family: var(--re-font-body);
  line-height: var(--re-leading-body);
  color: var(--re-text-color);
}

/* ============================================
   HR GRADIENT STYLES
   ============================================ */

/* Base HR Style - Gradient fade */
hr {
    height: 1px;
    border: none;
    background: linear-gradient(
        to right,
        transparent 0%,
        rgba(130, 107, 125, 0.3) 20%,
        rgba(130, 107, 125, 0.6) 50%,
        rgba(130, 107, 125, 0.3) 80%,
        transparent 100%
    );
    margin: 20px 0;
    opacity: 0.8;
}

/* Article Separator - More prominent */
.article-separator,
hr.article-separator {
    height: 1px;
    background: linear-gradient(
        to right,
        transparent 0%,
        rgba(130, 107, 125, 0.4) 15%,
        rgba(130, 107, 125, 0.8) 50%,
        rgba(130, 107, 125, 0.4) 85%,
        transparent 100%
    );
    margin: 40px 0;
}

/* Section Separator - Subtle */
.section-separator,
hr.section-separator {
    height: 1px;
    background: linear-gradient(
        to right,
        transparent 0%,
        rgba(130, 107, 125, 0.2) 25%,
        rgba(130, 107, 125, 0.4) 50%,
        rgba(130, 107, 125, 0.2) 75%,
        transparent 100%
    );
    margin: 30px 0;
}

/* Breadcrumb separator - Very subtle */
hr.breadcrumb-separator {
    height: 1px;
    background: linear-gradient(
        to right,
        transparent 0%,
        rgba(130, 107, 125, 0.1) 30%,
        rgba(130, 107, 125, 0.2) 50%,
        rgba(130, 107, 125, 0.1) 70%,
        transparent 100%
    );
    margin: 10px 0;
}

/* ============================================
   RESPONSIVE ADJUSTMENTS
   ============================================ */

@media (max-width: 768px) {
  :root {
    --spacing-lg: 16px;
    --spacing-xl: 24px;
    --spacing-2xl: 32px;
  }
  
  /*
   * There used to be a body background here, described as a "simpler
   * gradient on mobile for performance". It was not simpler - same shape,
   * same cost - it just still used the old broken pair of stops that the
   * rule at the top of this file was fixed to stop using:
   * var(--bs-body-color) is Bootstrap's TEXT colour, #adb5bd, a light grey.
   *
   * So every screen 768px and under faded from plum to pale grey and washed
   * the whole dark theme out, while desktop looked right. The fix at the top
   * had simply never been carried down into this block.
   *
   * It is deleted rather than corrected: it was a duplicate of the rule
   * above with no reason to differ, and a duplicate is what let the two
   * drift apart in the first place. The body gradient now has one
   * definition, and it applies at every width.
   */


  /* Adjust HR margins on mobile */
  hr {
      margin: 16px 0;
  }
  
  .article-separator,
  hr.article-separator {
      margin: 24px 0;
  }
  
  .section-separator,
  hr.section-separator {
      margin: 20px 0;
  }
}

/* ============================================
   ACCESSIBILITY
   ============================================ */

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    transition-duration: 0.01ms;
  }
}

@media (prefers-contrast: high) {
  :root {
    --glass-border: rgba(255, 255, 255, 0.3);
    --glass-blur: blur(20px) saturate(200%);
  }
  
  /* Higher contrast HR in high contrast mode */
  hr {
      background: linear-gradient(
          to right,
          transparent 0%,
          rgba(130, 107, 125, 0.6) 20%,
          rgba(130, 107, 125, 0.9) 50%,
          rgba(130, 107, 125, 0.6) 80%,
          transparent 100%
      );
  }
}

/* ============================================
   PRINT STYLES
   ============================================ */

@media print {
  body {
    background: white;
    color: black;
  }
  
  * {
    box-shadow: none;
    backdrop-filter: none;
  }
  
  /* Solid HR for printing */
  hr {
      height: 1px;
      background: #000;
      opacity: 0.3;
  }
}



/* ===== assets/css/navigation-final-fix.css ===== */
/**
 * Navigation Final Fix - Clean Implementation
 * Fixes all navigation issues:
 * 1. Nav links use Albert Sans 16px
 * 2. Dropdowns use Silkscreen 8px
 * 3. Proper clickability and z-index
 * 4. Fixed positioning at top
 * 5. Language dropdown renders over everything
 */

/* ============================================
   RESET ALL PREVIOUS NAVIGATION STYLES
   ============================================ */

/* Remove conflicting !important rules */
.navbar * {
    font-family: inherit;
    font-size: inherit;
}

/* ============================================
   NAVBAR BASE STYLES - FIXED AT TOP
   ============================================ */

.navbar.navbar-dark.navbar-expand-md.fixed-top {
    /* CRITICAL: Fixed positioning */
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    width: 100% !important;
    
    /* Appearance - Dark transparent glass matching music player */
    background: var(--re-surface);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    box-shadow: 
        0 8px 32px 0 rgba(0,0,0,0.5),
        inset 0 1px 0 0 rgba(255,255,255,0.18),
        inset 0 -1px 0 0 rgba(0,0,0,0.15);
    border-bottom: 1px solid var(--re-border);
    
    /* Z-index - below dropdowns */
    z-index: 1030 !important;
}

/* ============================================
   NAV LINKS - THE UI FACE
   ============================================ */

.navbar-nav .nav-link {
    /*
     * Silkscreen, like the rest of the site's chrome.
     *
     * These were the last significant run of body text in the furniture -
     * Albert Sans 16px sitting directly above a header strip, a footer and a
     * player that are all 8px Silkscreen, which made the navigation read as
     * though it belonged to a different site.
     *
     * No font-size here on purpose. silkscreen-pixel-grid.css holds every
     * Silkscreen run at 8px, and a number in this rule would only be a lie
     * about what renders. 8px is also the only honest answer: Silkscreen is
     * drawn on an 8px em, so the 11px this nearly became would have been
     * resampled and blurry - the exact thing the pixel grid exists to stop.
     */
    font-family: var(--re-font-ui) !important;
    font-weight: 400 !important;
    letter-spacing: 1px !important;
    text-transform: uppercase !important;
    
    /* Colors */
    color: var(--re-text-color) !important;
    text-shadow: 0px 1px rgba(0,0,0,0.5);
    
    /* Spacing */
    padding: 0.5rem 0.75rem !important;
    
    /* Interaction */
    cursor: pointer;
    pointer-events: auto;
    transition: color 0.3s ease;
    
    /* Layout */
    display: inline-flex !important;
    align-items: center;
    gap: 8px;
    position: relative;
}

/* Active state */
.navbar-nav .nav-link.active {
    color: #ffffff !important;
}

/* Hover state */
.navbar-nav .nav-link:hover {
    color: #ffffff !important;
}

/* Underline effect */
.navbar-nav .nav-link::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 0;
    height: 2px;
    background: linear-gradient(90deg, var(--re-brand-400) 0%, var(--bs-link-hover-color) 100%);
    transition: width 0.3s ease;
    border-radius: var(--re-radius-xs);
}

.navbar-nav .nav-link:hover::after,
.navbar-nav .nav-link.active::after {
    width: 80%;
}

/* ============================================
   DROPDOWN TOGGLE - ALBERT SANS 16PX
   ============================================ */

.navbar-nav .dropdown-toggle {
    /* Typography */
    font-family: var(--re-font-body) !important;
    font-size: 16px !important;
    font-weight: 400 !important;
    letter-spacing: normal !important;
    
    /* Colors */
    color: var(--re-brand-400) !important;
    text-shadow: 0px 1px rgba(0,0,0,0.5);
    
    /* Spacing */
    padding: 0.5rem 1rem !important;
    
    /* Interaction */
    cursor: pointer;
    pointer-events: auto;
    transition: color 0.3s ease;
    
    /* Layout */
    display: inline-flex !important;
    align-items: center;
    gap: 8px;
    position: relative;
}

/* Active/Open state */
.navbar-nav .dropdown-toggle.active,
.navbar-nav .dropdown-toggle[aria-expanded="true"] {
    color: #ffffff !important;
}

/* Hover state */
.navbar-nav .dropdown-toggle:hover {
    color: #ffffff !important;
}

/* Remove Bootstrap arrow */
.navbar-nav .dropdown-toggle::after {
    display: none;
}

/* Underline effect */
.navbar-nav .dropdown-toggle::before {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 0;
    height: 2px;
    background: linear-gradient(90deg, var(--re-brand-400) 0%, var(--bs-link-hover-color) 100%);
    transition: width 0.3s ease;
    border-radius: var(--re-radius-xs);
}

.navbar-nav .dropdown-toggle:hover::before,
.navbar-nav .dropdown-toggle[aria-expanded="true"]::before {
    width: 80%;
}

/* ============================================
   DROPDOWN MENU
   ============================================ */

.navbar .dropdown-menu {
    /* Positioning */
    position: absolute !important;
    top: 100% !important;
    left: 50% !important;
    transform: translateX(-50%) !important;
    margin-top: 8px;
    
    /* Dark Refractive Glass Effect - matching music player */
    background: rgba(20, 15, 22, 0.85) !important;
    backdrop-filter: var(--re-glass) !important;
    -webkit-backdrop-filter: var(--re-glass) !important;
    
    /* Multi-layer glass borders - purple tinted */
    border: 1px solid var(--re-border) !important;
    border-radius: var(--re-radius-md) !important;
    padding: 8px 0 !important;
    
    box-shadow: 
        0 8px 32px 0 rgba(0, 0, 0, 0.5),
        inset 0 1px 0 0 rgba(255, 255, 255, 0.18),
        inset 0 -1px 0 0 rgba(0, 0, 0, 0.15) !important;
    
    /* Sizing */
    min-width: 220px !important;
    width: max-content !important;
    max-width: 280px !important;
    
    /* Z-index - ABOVE EVERYTHING */
    z-index: 9999 !important;
    
    /* Interaction */
    pointer-events: auto !important;
    
    /* Glass noise texture for refraction effect */
    position: relative;
}

/* Glass noise texture overlay */
.navbar .dropdown-menu::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilterDropdown'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilterDropdown)'/%3E%3C/svg%3E");
    opacity: 0.06;
    pointer-events: none;
    z-index: 1;
    border-radius: var(--re-radius-md);
}

/* Glass highlight on top edge */
.navbar .dropdown-menu::after {
    content: '';
    position: absolute;
    top: 1px;
    left: 8px;
    right: 8px;
    height: 1px;
    background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
    pointer-events: none;
    z-index: 2;
}

/* Show dropdown */
.navbar .dropdown.show .dropdown-menu {
    display: block !important;
}

/* ============================================
   DROPDOWN ITEMS - SILKSCREEN 6PX ONLY HERE
   ============================================ */

.navbar .dropdown-menu .dropdown-item {
    /* Typography - SILKSCREEN 6PX */
    font-family: var(--re-font-ui) !important;
    font-size: 6px !important;
    text-transform: uppercase !important;
    letter-spacing: 0.5px !important;
    
    /* Colors */
    color: var(--re-brand-400) !important;
    text-shadow: 0px 1px rgba(0,0,0,0.5);
    
    /* Spacing */
    padding: 10px 16px !important;
    
    /* Interaction */
    transition: all 0.2s ease;
    cursor: pointer;
    pointer-events: auto;
    
    /* Layout */
    display: flex !important;
    align-items: center !important;
    gap: 10px !important;
    white-space: nowrap !important;
    
    /* Z-index - ensure items are above glass effect */
    position: relative;
    z-index: 3;
}

/* Hover state - Enhanced glass effect */
.navbar .dropdown-menu .dropdown-item:hover,
.navbar .dropdown-menu .dropdown-item:focus {
    color: #ffffff !important;
    background: var(--re-surface-hover) !important;
    backdrop-filter: var(--re-glass-subtle) !important;
    -webkit-backdrop-filter: var(--re-glass-subtle) !important;
}

/* Active state - Enhanced glass effect */
.navbar .dropdown-menu .dropdown-item.active,
.navbar .dropdown-menu .dropdown-item:active {
    color: #ffffff !important;
    background: var(--re-surface-active) !important;
    backdrop-filter: var(--re-glass-subtle) !important;
    -webkit-backdrop-filter: var(--re-glass-subtle) !important;
}

/* Icons in dropdown items */
.navbar .dropdown-menu .dropdown-item i {
    color: inherit;
    font-size: 14px;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

/* ============================================
   DROPDOWN DIVIDER
   ============================================ */

.navbar .dropdown-menu .dropdown-divider {
    border-color: var(--re-border) !important;
    margin: 4px 0 !important;
}

/* ============================================
   NAVBAR CONTAINER
   ============================================ */

.navbar .container {
    position: relative;
    overflow: visible !important;
    z-index: 2;
}

/* Ensure dropdown parent has proper z-index */
.navbar-nav .nav-item.dropdown {
    position: relative;
    z-index: 1000;
}

/* ============================================
   MOBILE RESPONSIVE
   ============================================ */

@media (max-width: 991px) {
    /* Remove underline effects on mobile */
    .navbar-nav .nav-link::after,
    .navbar-nav .dropdown-toggle::before {
        display: none;
    }
    
    /* Stack dropdown menu on mobile */
    .navbar .dropdown-menu {
        position: static !important;
        transform: none !important;
        left: auto !important;
        margin-top: 4px !important;
        width: 100% !important;
        max-width: 100% !important;
    }
}

/* ============================================
   LANGUAGE DROPDOWN (USER SECTION) - DARK REFRACTIVE GLASS
   ============================================ */

.dropdown.header-controls-lang {
    position: relative;
    z-index: 10000 !important;
}

.dropdown.header-controls-lang .dropdown-menu {
    /* Positioning */
    position: absolute !important;
    right: 0 !important;
    left: auto !important;
    transform: none !important;
    
    /* Z-index - ABOVE BREADCRUMBS AND ALL PANELS */
    z-index: 10001 !important;
    
    /* Dark Refractive Glass Effect - matching music player */
    background: rgba(20, 15, 22, 0.85) !important;
    backdrop-filter: blur(16px) saturate(180%) contrast(110%) !important;
    -webkit-backdrop-filter: blur(16px) saturate(180%) contrast(110%) !important;
    
    /* Multi-layer glass borders - purple tinted */
    border: 1px solid rgba(130, 107, 125, 0.35) !important;
    border-radius: var(--re-radius-md) !important;
    padding: 8px 0 !important;
    
    box-shadow: 
        0 12px 40px 0 rgba(0, 0, 0, 0.6),
        inset 0 1px 0 0 rgba(255, 255, 255, 0.18),
        inset 0 -1px 0 0 rgba(0, 0, 0, 0.15) !important;
    
    /* Ensure it's always on top */
    pointer-events: auto !important;
    
    /* Glass noise texture for refraction effect */
    position: relative;
}

/* Glass noise texture overlay */
.dropdown.header-controls-lang .dropdown-menu::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilterLang'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilterLang)'/%3E%3C/svg%3E");
    opacity: 0.06;
    pointer-events: none;
    z-index: 1;
    border-radius: var(--re-radius-md);
}

/* Glass highlight on top edge */
.dropdown.header-controls-lang .dropdown-menu::after {
    content: '';
    position: absolute;
    top: 1px;
    left: 8px;
    right: 8px;
    height: 1px;
    background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
    pointer-events: none;
    z-index: 2;
}

.dropdown.header-controls-lang .dropdown-item {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    color: var(--re-brand-400) !important;
    padding: 10px 16px !important;
    transition: all 0.2s ease !important;
    
    /* Ensure items are above glass effect */
    position: relative;
    z-index: 3;
}

.dropdown.header-controls-lang .dropdown-item:hover {
    color: #ffffff !important;
    background: rgba(130, 107, 125, 0.4) !important;
}

/* ============================================
   CLICKABILITY FIX
   ============================================ */

/* Ensure all nav elements are clickable */
.navbar-nav,
.navbar-nav .nav-item,
.navbar-nav .nav-link,
.navbar-nav .dropdown-toggle,
.navbar .dropdown-menu,
.navbar .dropdown-menu .dropdown-item {
    pointer-events: auto !important;
}

/* Remove any overlays that might block clicks */
.navbar::before,
.navbar::after {
    pointer-events: none !important;
}


/* ===== assets/css/critical-rendering-fixes.css ===== */
/**
 * Critical Rendering Fixes
 * Fixes broken alternating articles and site messages
 */

/* ============================================
   ALTERNATING ARTICLES FIX
   ============================================ */

/* Remove any !important that might break layout */
.article-container-dark,
.article-container-light {
    /* Use defined styles without conflicts */
    background: rgba(0,0,0,0.1) !important;
    backdrop-filter: blur(12px) saturate(180%) contrast(110%) !important;
    -webkit-backdrop-filter: blur(12px) saturate(180%) contrast(110%) !important;
    border: 1px solid var(--re-border) !important;
    box-shadow: 
        0 8px 32px 0 rgba(0, 0, 0, 0.5),
        inset 0 1px 0 0 rgba(255, 255, 255, 0.15),
        inset 0 -1px 0 0 rgba(0, 0, 0, 0.15) !important;
    border-radius: var(--re-radius-lg) !important;
    padding: 32px !important;
    margin-bottom: 32px !important;
    position: relative;
    overflow: visible !important;
}

/* Ensure article content is visible */
.article-container-dark *,
.article-container-light * {
    color: var(--re-text-color) !important;
}

/* Subheadings inside a post - body face, so long-form writing stays readable. */
.article-container-dark h2,
.article-container-light h2 {
    color: var(--re-brand-400) !important;
    font-family: var(--re-font-body) !important;
    font-size: clamp(24px, 4vw, 32px) !important;
    font-weight: 700 !important;
    margin-bottom: 16px !important;
}

/*
 * The post's own title takes the display face, matching the devlog list it
 * was clicked from. A title that changes face between the list and the page
 * reads as two different posts.
 *
 * This rule is where the change belongs. The h1 carries an inline
 * font-family, japanese-support.css forces the body face on .article-title,
 * and theme-color-fix.css forces the display face on every heading - and all
 * three lose to the (0,2,0) selector here. Setting the face anywhere else
 * would have had no effect at all.
 *
 * font-weight normal, deliberately: ThaleahFat has a single weight, so asking
 * for 700 gets a synthesised bold that smears a pixel face. theme-color-fix's
 * site-wide heading rule makes the same choice for the same reason.
 */
.article-container-dark .article-title,
.article-container-light .article-title {
    color: var(--re-brand-400) !important;
    font-family: var(--re-font-display) !important;
    font-size: clamp(28px, 5vw, 44px) !important;
    font-weight: normal !important;
    line-height: 1.15 !important;
    margin-bottom: 16px !important;
}

/* Article text */
.article-container-dark p,
.article-container-light p,
.article-container-dark .article-excerpt,
.article-container-light .article-excerpt {
    color: var(--re-text-color) !important;
    font-family: var(--re-font-body) !important;
    font-size: 16px !important;
    line-height: 1.7 !important;
}

/* Article meta (dates, categories) */
.article-meta,
.article-meta-item,
.article-category,
.article-date {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    text-transform: uppercase !important;
    letter-spacing: 0.5px !important;
    color: var(--re-text-color) !important;
    opacity: 0.8 !important;
}

/* Article buttons */
.article-read-more,
.article-container-dark .btn,
.article-container-light .btn {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    text-transform: uppercase !important;
    letter-spacing: 1px !important;
    padding: 8px 16px !important;
    background: rgba(130,107,125,0.3) !important;
    border: 1px solid var(--re-border-strong) !important;
    border-radius: var(--re-radius-sm) !important;
    color: var(--re-text-color) !important;
    transition: all 0.3s ease !important;
    display: inline-flex !important;
    align-items: center !important;
    gap: 8px !important;
    text-decoration: none !important;
}

.article-read-more:hover,
.article-container-dark .btn:hover,
.article-container-light .btn:hover {
    background: var(--re-surface-active) !important;
    border-color: rgba(130,107,125,0.7) !important;
    transform: translateY(-2px) !important;
    color: var(--re-text-color) !important;
}

/* Featured badge */
.article-featured-badge {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    background: rgba(255,193,7,0.3) !important;
    border: 1px solid rgba(255,193,7,0.5) !important;
    color: #ffc107 !important;
    padding: 4px 12px !important;
    border-radius: var(--re-radius-lg) !important;
    text-transform: uppercase !important;
    letter-spacing: 0.5px !important;
}

/* Article images */
.article-container-dark img,
.article-container-light img {
    border-radius: var(--re-radius-lg) !important;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
    max-width: 100% !important;
    height: auto !important;
}

/* ============================================
   SITE MESSAGES FIX
   ============================================ */

/* Base site message styling */
.site-message {
    background: var(--re-surface) !important;
    backdrop-filter: blur(12px) saturate(180%) contrast(110%) !important;
    -webkit-backdrop-filter: blur(12px) saturate(180%) contrast(110%) !important;
    border: 1px solid var(--re-border) !important;
    border-radius: var(--re-radius-lg) !important;
    padding: 20px !important;
    margin-bottom: 20px !important;
    display: flex !important;
    align-items: flex-start !important;
    gap: 16px !important;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
    position: relative;
    overflow: hidden;
}

/* Message icon */
.site-message-icon {
    flex-shrink: 0;
    font-size: 24px !important;
    width: 32px !important;
    height: 32px !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
}

/* Message content */
.site-message-content {
    flex: 1;
    min-width: 0;
}

/* Message title */
.site-message-title {
    font-family: var(--re-font-body) !important;
    font-size: 18px !important;
    font-weight: 700 !important;
    margin-bottom: 8px !important;
    color: var(--re-text-color) !important;
}

/* Message text */
.site-message-text {
    font-family: var(--re-font-body) !important;
    font-size: 14px !important;
    line-height: 1.6 !important;
    color: var(--re-text-color) !important;
    opacity: 0.9 !important;
}

/* Message close button */
.site-message-close {
    flex-shrink: 0;
    background: transparent !important;
    border: none !important;
    color: var(--re-text-color) !important;
    opacity: 0.6 !important;
    cursor: pointer !important;
    font-size: 20px !important;
    padding: 4px !important;
    transition: opacity 0.2s ease !important;
}

.site-message-close:hover {
    opacity: 1 !important;
}

/* Message type variants */
.site-message-info {
    border-color: rgba(23,162,184,0.5) !important;
    background: linear-gradient(135deg, rgba(23,162,184,0.15), rgba(23,162,184,0.05)) !important;
}

.site-message-info .site-message-icon {
    color: #17a2b8 !important;
}

.site-message-success {
    border-color: rgba(40,167,69,0.5) !important;
    background: linear-gradient(135deg, rgba(40,167,69,0.15), rgba(40,167,69,0.05)) !important;
}

.site-message-success .site-message-icon {
    color: #28a745 !important;
}

.site-message-warning {
    border-color: rgba(255,193,7,0.5) !important;
    background: linear-gradient(135deg, rgba(255,193,7,0.15), rgba(255,193,7,0.05)) !important;
}

.site-message-warning .site-message-icon {
    color: #ffc107 !important;
}

.site-message-danger {
    border-color: rgba(220,53,69,0.5) !important;
    background: linear-gradient(135deg, rgba(220,53,69,0.15), rgba(220,53,69,0.05)) !important;
}

.site-message-danger .site-message-icon {
    color: #dc3545 !important;
}

/* ============================================
   RESPONSIVE FIXES
   ============================================ */

@media (max-width: 768px) {
    /* Articles on mobile */
    .article-container-dark,
    .article-container-light {
        padding: 20px !important;
        margin-bottom: 20px !important;
    }
    
    .article-container-dark h2,
    .article-container-light h2 {
        font-size: 20px !important;
    }
    
    .article-container-dark p,
    .article-container-light p {
        font-size: 14px !important;
    }
    
    /* Messages on mobile */
    .site-message {
        flex-direction: column !important;
        padding: 16px !important;
    }
    
    .site-message-icon {
        font-size: 20px !important;
    }
    
    .site-message-title {
        font-size: 16px !important;
    }
    
    .site-message-text {
        font-size: 13px !important;
    }
}

/* ============================================
   ACCESSIBILITY
   ============================================ */

/* Ensure proper contrast */
@media (prefers-contrast: high) {
    .article-container-dark *,
    .article-container-light *,
    .site-message * {
        color: var(--re-text-color) !important;
        border-color: rgba(255,255,255,0.5) !important;
    }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
    .article-container-dark,
    .article-container-light,
    .site-message,
    .article-read-more {
        transition: none !important;
        animation: none !important;
    }
}


/* ===== assets/css/site-messages.css ===== */
/**
 * Site Messages Component - Glass Morphism Design
 * Modern frosted glass style matching navigation, footer, and music player
 */

/* ============================================
   SITE MESSAGE CONTAINER
   ============================================ */

.site-message-container {
  width: 100%;
  margin-bottom: 24px;
  padding: 20px 32px;
  border-radius: var(--re-radius-lg);
  position: relative;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  
  /* Glass morphism effect */
  background: var(--re-surface);
  backdrop-filter: var(--re-glass);
  -webkit-backdrop-filter: var(--re-glass);
  
  /* Glass borders and shadows */
  border: 1px solid rgba(255, 255, 255, 0.25);
  box-shadow: 
    0 8px 32px 0 rgba(0, 0, 0, 0.5),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.15),
    inset 0 -1px 0 0 rgba(0, 0, 0, 0.15);
  
  /* Animation */
  animation: slideDown 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
  opacity: 0;
  transform: translateY(-20px);
}

.site-message-container.show {
  opacity: 1;
  transform: translateY(0);
}

/* ============================================
   MESSAGE TYPE VARIANTS
   ============================================ */

/* INFO - Cyan */
.site-message-container[data-message-type="info"],
.site-message-container.message-info {
  border-color: rgba(23, 162, 184, 0.5);
  background: rgba(23, 162, 184, 0.15);
}

.site-message-container[data-message-type="info"]::before {
  background: linear-gradient(135deg, rgba(23, 162, 184, 0.3) 0%, rgba(23, 162, 184, 0.1) 100%);
}

/* SUCCESS - Green */
.site-message-container[data-message-type="success"],
.site-message-container.message-success {
  border-color: rgba(40, 167, 69, 0.5);
  background: rgba(40, 167, 69, 0.15);
}

.site-message-container[data-message-type="success"]::before {
  background: linear-gradient(135deg, rgba(40, 167, 69, 0.3) 0%, rgba(40, 167, 69, 0.1) 100%);
}

/* WARNING - Yellow */
.site-message-container[data-message-type="warning"],
.site-message-container.message-warning {
  border-color: rgba(255, 193, 7, 0.5);
  background: rgba(255, 193, 7, 0.15);
}

.site-message-container[data-message-type="warning"]::before {
  background: linear-gradient(135deg, rgba(255, 193, 7, 0.3) 0%, rgba(255, 193, 7, 0.1) 100%);
}

/* DANGER - Red */
.site-message-container[data-message-type="danger"],
.site-message-container.message-danger {
  border-color: rgba(220, 53, 69, 0.5);
  background: rgba(220, 53, 69, 0.15);
}

.site-message-container[data-message-type="danger"]::before {
  background: linear-gradient(135deg, rgba(220, 53, 69, 0.3) 0%, rgba(220, 53, 69, 0.1) 100%);
}

/* MAINTENANCE - Purple */
.site-message-container[data-message-type="maintenance"],
.site-message-container.message-maintenance {
  border-color: var(--re-border-strong);
  background: var(--re-surface-raised);
  animation: slideDown 0.5s cubic-bezier(0.34, 1.56, 0.64, 1), pulse-maintenance 2s ease-in-out infinite 1s;
}

.site-message-container[data-message-type="maintenance"]::before {
  background: linear-gradient(135deg, rgba(130, 107, 125, 0.3) 0%, rgba(130, 107, 125, 0.1) 100%);
}

/* Glass noise texture overlay */
.site-message-container::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url('data:image/svg+xml,%3Csvg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg"%3E%3Cfilter id="noiseFilter"%3E%3CfeTurbulence type="fractalNoise" baseFrequency="4.5" numOctaves="5" stitchTiles="stitch"/%3E%3C/filter%3E%3Crect width="100%25" height="100%25" filter="url(%23noiseFilter)"/%3E%3C/svg%3E');
  opacity: 0.05;
  pointer-events: none;
  border-radius: var(--re-radius-lg);
}

/* Glass highlight on top */
.site-message-container::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
  pointer-events: none;
  border-radius: var(--re-radius-lg) var(--re-radius-lg) 0 0;
}

/* ============================================
   MESSAGE CONTENT LAYOUT
   ============================================ */

.site-message-container > div:first-of-type {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  position: relative;
  z-index: 2;
}

/* Icon container */
.site-message-container .message-icon {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
  opacity: 0.9;
}

/* Message content wrapper */
.site-message-container .message-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Message title */
.site-message-container h4 {
  color: #ffffff;
  font-family: var(--re-font-body);
  font-size: 20px;
  font-weight: 700;
  margin: 0;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  letter-spacing: 0.5px;
}

/* Message text */
.site-message-container p {
  color: #ffffff;
  font-family: var(--re-font-body);
  font-size: 16px;
  line-height: 1.6;
  margin: 0;
  text-shadow: 0 1px rgba(0, 0, 0, 0.5);
  opacity: 0.95;
}

/* Message metadata */
.site-message-container .message-meta {
  margin-top: 12px;
  font-family: var(--re-font-ui);
  font-size: 8px;
  opacity: 0.8;
  color: #ffffff;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  -webkit-font-smoothing: none;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeSpeed;
}

/* ============================================
   ACTION BUTTONS
   ============================================ */

/* Dismiss button */
.site-message-dismiss {
  flex-shrink: 0;
  padding: 10px 16px;
  background: rgba(255, 255, 255, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: var(--re-radius-md);
  color: #ffffff;
  font-family: var(--re-font-ui);
  font-size: 7px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  
  backdrop-filter: var(--re-glass-subtle);
  -webkit-backdrop-filter: var(--re-glass-subtle);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.site-message-dismiss:hover {
  background: rgba(255, 255, 255, 0.25);
  border-color: rgba(255, 255, 255, 0.4);
  transform: translateY(-2px);
  box-shadow: 
    0 4px 12px rgba(0, 0, 0, 0.3),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

.site-message-dismiss:active {
  transform: translateY(0);
  box-shadow: 
    0 2px 4px rgba(0, 0, 0, 0.2),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.site-message-dismiss:focus {
  outline: 2px solid rgba(255, 255, 255, 0.4);
  outline-offset: 2px;
}

.site-message-dismiss i {
  font-size: 14px;
  line-height: 1;
}

/* ============================================
   PRIORITY BADGE
   ============================================ */

.site-message-priority {
  position: absolute;
  bottom: 12px;
  right: 32px;
  padding: 6px 14px;
  border-radius: var(--re-radius-lg);
  font-family: var(--re-font-ui);
  font-size: 8px;
  text-transform: uppercase;
  letter-spacing: 1px;
  font-weight: 700;
  color: #ffffff;
  backdrop-filter: var(--re-glass-subtle);
  -webkit-backdrop-filter: var(--re-glass-subtle);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  z-index: 10;
}

/* Priority badge colors */
.site-message-container[data-message-type="maintenance"] .site-message-priority {
  background: rgba(130, 107, 125, 0.8);
  border: 1px solid var(--re-border-strong);
}

.site-message-container[data-message-type="danger"] .site-message-priority {
  background: rgba(220, 53, 69, 0.8);
  border: 1px solid rgba(220, 53, 69, 0.6);
}

/* ============================================
   ANIMATIONS
   ============================================ */

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideUp {
  from {
    opacity: 1;
    transform: translateY(0);
  }
  to {
    opacity: 0;
    transform: translateY(-30px);
  }
}

@keyframes pulse-maintenance {
  0%, 100% {
    box-shadow: 
      0 8px 32px 0 rgba(130, 107, 125, 0.3),
      inset 0 1px 0 0 rgba(255, 255, 255, 0.15),
      inset 0 -1px 0 0 rgba(0, 0, 0, 0.15);
  }
  50% {
    box-shadow: 
      0 8px 40px 0 rgba(130, 107, 125, 0.5),
      inset 0 1px 0 0 rgba(255, 255, 255, 0.2),
      inset 0 -1px 0 0 rgba(0, 0, 0, 0.2);
  }
}

/* Exit animation */
.site-message-container.exit {
  animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

/* ============================================
   RESPONSIVE DESIGN
   ============================================ */

@media (max-width: 1024px) {
  .site-message-container {
    padding: 18px 28px;
  }

  .site-message-container h4 {
    font-size: 18px;
  }

  .site-message-container p {
    font-size: 15px;
  }

  .site-message-container .message-icon {
    font-size: 28px;
  }

  .site-message-priority {
    bottom: 16px;
    right: 24px;
    padding: 4px 12px;
    font-size: 7px;
  }
}

@media (max-width: 768px) {
  .site-message-container {
    padding: 16px 20px;
    margin-bottom: 16px;
  }

  .site-message-container > div:first-of-type {
    gap: 12px;
  }

  .site-message-container h4 {
    font-size: 16px;
    margin-bottom: 4px;
  }

  .site-message-container p {
    font-size: 14px;
  }

  .site-message-container .message-icon {
    font-size: 24px;
  }

  .site-message-dismiss {
    padding: 8px 12px;
    font-size: 6px;
    gap: 4px;
  }

  .site-message-dismiss i {
    font-size: 12px;
  }

  .site-message-priority {
    display: none;
  }
}

@media (max-width: 480px) {
  .site-message-container {
    padding: 14px 16px;
  }

  .site-message-container h4 {
    font-size: 14px;
  }

  .site-message-container p {
    font-size: 12px;
  }

  .site-message-container .message-meta {
    font-size: 7px;
  }

  .site-message-container .message-icon {
    font-size: 20px;
  }

  .site-message-dismiss {
    padding: 6px 10px;
    font-size: 5px;
  }
}

/* ============================================
   ACCESSIBILITY
   ============================================ */

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .site-message-container {
    animation: none;
    opacity: 1;
    transform: none;
  }

  .site-message-dismiss {
    transition: none;
  }
}

/* High contrast mode */
@media (prefers-contrast: more) {
  .site-message-container {
    border-width: 2px;
    backdrop-filter: blur(20px) saturate(200%);
    -webkit-backdrop-filter: blur(20px) saturate(200%);
  }

  .site-message-dismiss {
    border-width: 1.5px;
  }
}

/* Dark mode system preference */
@media (prefers-color-scheme: dark) {
  .site-message-container {
    background: rgba(0, 0, 0, 0.5);
    border-color: rgba(255, 255, 255, 0.2);
  }
}

/* Light mode system preference */
@media (prefers-color-scheme: light) {
  .site-message-container {
    background: rgba(255, 255, 255, 0.85);
    border-color: rgba(0, 0, 0, 0.1);
  }

  .site-message-container h4,
  .site-message-container p {
    color: #212529;
  }
}


/* ===== assets/css/standardized-input-heights.css ===== */
/**
 * Standardized Input Heights
 * Ensures all single-line text inputs have consistent height
 * Multi-line textareas are excluded from this standardization
 * 
 * Target height: 44px (minimum recommended for accessibility)
 * Consistent padding: 12px vertical, 16px horizontal
 */

/* ============================================
   CORE INPUT STANDARDIZATION
   ============================================ */

/* All single-line text inputs */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="url"],
input[type="tel"],
input[type="number"],
input[type="date"],
input[type="datetime-local"],
input[type="month"],
input[type="time"],
input[type="week"],
input[type="search"],
select.form-select,
select.form-control,
.form-control:not(textarea) {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
    line-height: 1.5 !important;
    font-size: 14px !important;
}

/* Textareas remain flexible - NOT standardized */
textarea.form-control,
textarea {
    height: auto !important;
    min-height: 120px !important;
    padding: 12px 16px !important;
    line-height: 1.6 !important;
    font-size: 14px !important;
}

/* ============================================
   BOOTSTRAP FORM CONTROLS
   ============================================ */

/* Bootstrap form-control class */
.form-control {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* Exception for textarea */
textarea.form-control {
    height: auto !important;
    min-height: 120px !important;
}

/* Bootstrap select */
.form-select {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* ============================================
   ADMIN PANEL INPUTS
   ============================================ */

/* Admin form controls */
.admin-form-control {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* Admin form select */
.admin-form-select {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* Exception for admin textareas */
textarea.admin-form-control {
    height: auto !important;
    min-height: 120px !important;
}

/* ============================================
   INLINE STYLED INPUTS (from templates)
   ============================================ */

/* Override inline padding styles */
input[style*="padding: 12px"],
input[style*="padding: 14px"],
input[style*="padding: 16px"],
input[style*="padding:12px"],
input[style*="padding:14px"],
input[style*="padding:16px"] {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* Project editor title (large font exception) */
input[style*="font-size: 1.75rem"],
input[style*="font-size: 1.5rem"] {
    height: auto !important;
    min-height: 56px !important;
    padding: 14px 16px !important;
}

/* ============================================
   SPECIFIC FORM CONTEXTS
   ============================================ */

/* Login/Register forms */
#loginForm input[type="text"],
#loginForm input[type="password"],
#loginForm input[type="email"],
#registerForm input[type="text"],
#registerForm input[type="password"],
#registerForm input[type="email"] {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* Contact form */
#contact-name,
#contact-email,
#contact-subject {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* Contact message textarea - keep flexible */
#contact-message {
    height: auto !important;
    min-height: 150px !important;
    padding: 12px 16px !important;
}

/* Article/Project editor forms */
#title,
#slug,
#author,
#featured_image,
#project_url,
#github_url,
#developer_name,
#publisher_name,
#genre,
#type,
#platforms,
#modes,
#technologies,
#release_date,
#start_date,
#end_date,
#display_order,
#metacritic_score,
#metacritic_url {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* Large title inputs (project/article editors) */
#title[style*="font-size: 1.75rem"],
#title[style*="font-size: 1.5rem"] {
    height: auto !important;
    min-height: 56px !important;
    padding: 14px 16px !important;
}

/* Content/Description textareas - keep flexible */
#content,
#description,
#excerpt,
#message,
textarea[name="content"],
textarea[name="description"],
textarea[name="excerpt"] {
    height: auto !important;
    min-height: 120px !important;
    padding: 12px 16px !important;
}

/* ============================================
   SEARCH & FILTERS
   ============================================ */

/* Search inputs */
input[type="search"],
.search-input,
.filter-input {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* ============================================
   NUMBER INPUTS
   ============================================ */

/* Number inputs (display order, scores, etc) */
input[type="number"] {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* Large number displays (Metacritic score) */
input[type="number"][style*="font-size: 1.5rem"],
input[type="number"][style*="font-size: 1.2rem"] {
    height: auto !important;
    min-height: 56px !important;
    padding: 14px 16px !important;
    text-align: center !important;
}

/* ============================================
   DATE & TIME INPUTS
   ============================================ */

/* Date/time inputs */
input[type="date"],
input[type="datetime-local"],
input[type="month"],
input[type="time"],
input[type="week"] {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* ============================================
   URL & EMAIL INPUTS
   ============================================ */

/* URL inputs (monospace font) */
input[type="url"],
input[style*="font-family: monospace"],
input[style*="font-family: 'Courier New'"] {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* ============================================
   READONLY & DISABLED INPUTS
   ============================================ */

/* Readonly inputs (contact form) */
input[readonly],
input:read-only {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* Disabled inputs */
input:disabled,
input[disabled] {
    height: 44px !important;
    min-height: 44px !important;
    padding: 12px 16px !important;
}

/* ============================================
   INPUT GROUPS & ADDONS
   ============================================ */

/* Input groups */
.input-group input,
.input-group select,
.input-group .form-control,
.input-group .form-select {
    height: 44px !important;
    min-height: 44px !important;
}

/* Input group addons */
.input-group-text {
    min-height: 44px !important;
    padding: 12px 16px !important;
    display: flex !important;
    align-items: center !important;
}

/* ============================================
   SMALL & LARGE SIZE VARIANTS
   ============================================ */

/* Small inputs (if explicitly marked) */
.form-control-sm,
.form-select-sm,
input.input-sm {
    height: 36px !important;
    min-height: 36px !important;
    padding: 8px 12px !important;
    font-size: 13px !important;
}

/* Large inputs (if explicitly marked) */
.form-control-lg,
.form-select-lg,
input.input-lg {
    height: 52px !important;
    min-height: 52px !important;
    padding: 14px 20px !important;
    font-size: 16px !important;
}

/* ============================================
   MOBILE RESPONSIVE
   ============================================ */

@media (max-width: 768px) {
    /* Slightly larger inputs on mobile for better touch targets */
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="url"],
    input[type="tel"],
    input[type="number"],
    input[type="date"],
    input[type="search"],
    select.form-select,
    select.form-control,
    .form-control:not(textarea) {
        height: 48px !important;
        min-height: 48px !important;
        padding: 14px 16px !important;
        font-size: 16px !important; /* Prevents iOS zoom */
    }
    
    /* Textareas remain flexible */
    textarea.form-control,
    textarea {
        min-height: 120px !important;
        font-size: 16px !important; /* Prevents iOS zoom */
    }
}

@media (max-width: 480px) {
    /* Even larger touch targets on small phones */
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="url"],
    input[type="tel"],
    input[type="number"],
    input[type="date"],
    input[type="search"],
    select.form-select,
    select.form-control,
    .form-control:not(textarea) {
        height: 52px !important;
        min-height: 52px !important;
        padding: 16px 16px !important;
        font-size: 16px !important;
    }
}

/* ============================================
   FOCUS STATE CONSISTENCY
   ============================================ */

/* All inputs have consistent focus state height */
input:focus,
select:focus,
.form-control:focus,
.form-select:focus {
    /* Height already set above, just ensure no override */
    height: inherit !important;
    min-height: inherit !important;
}

/* ============================================
   EXCEPTIONS & EDGE CASES
   ============================================ */

/* File inputs - different layout */
input[type="file"] {
    height: auto !important;
    min-height: 44px !important;
    padding: 10px 16px !important;
}

/* Checkboxes & Radio buttons - preserve size */
input[type="checkbox"],
input[type="radio"] {
    height: 20px !important;
    width: 20px !important;
    min-height: 20px !important;
    padding: 0 !important;
}

/* Color picker */
input[type="color"] {
    height: 44px !important;
    min-height: 44px !important;
    padding: 4px !important;
}

/* Range slider */
input[type="range"] {
    height: auto !important;
    min-height: auto !important;
    padding: 0 !important;
}

/* Hidden inputs */
input[type="hidden"] {
    height: auto !important;
    min-height: auto !important;
    padding: 0 !important;
    display: none !important;
}

/* ============================================
   ACCESSIBILITY
   ============================================ */

/* High contrast mode - ensure borders are visible */
@media (prefers-contrast: high) {
    input,
    select,
    .form-control,
    .form-select {
        border-width: 2px !important;
    }
}

/* Reduced motion - no transition on height changes */
@media (prefers-reduced-motion: reduce) {
    input,
    select,
    .form-control,
    .form-select {
        transition: none !important;
    }
}

/* ============================================
   PRINT STYLES
   ============================================ */

@media print {
    input,
    select,
    .form-control,
    .form-select {
        height: auto !important;
        min-height: auto !important;
        border: 1px solid #000 !important;
        background: #fff !important;
        color: #000 !important;
    }
}


/* ===== assets/css/glass-standard.css ===== */
/**
 * STANDARDIZED GLASS MORPHISM SYSTEM
 * ===================================
 * Single source of truth for all glass effects site-wide.
 * Matches the music player's premium refractive glass standard.
 * 
 * DESIGN SPEC:
 * - backdrop-filter: blur(16px) saturate(180%) contrast(110%)
 * - Multi-layer borders with inset shadows (dark border accents)
 * - SVG noise texture overlays (opacity: 0.06)
 * - Glass highlight (rim light) on top edge
 * - Unified 8px border-radius (12px for larger containers)
 * 
 * THEME SUPPORT:
 * - Dark theme: rgba(0, 0, 0, 0.15) backgrounds
 * - Light theme: rgba(130, 107, 125, 0.15) backgrounds
 * 
 * @author RealityEngine
 * @version 3.2
 */

/* ============================================
   CSS CUSTOM PROPERTIES - GLASS SYSTEM
   ============================================ */

/*
 * These --glass-* names are kept because ~60 rules across the site already use
 * them. They are now thin aliases over the design tokens in theme-tokens.css so
 * there is one place to change the look, not two competing vocabularies.
 * Prefer the --re-* tokens directly in new code.
 */
:root {
  --glass-bg: var(--re-surface);
  --glass-bg-light: var(--re-surface-raised);
  --glass-bg-hover: var(--re-surface-hover);
  --glass-bg-dark: var(--re-surface-sunken);

  --glass-blur: var(--re-glass);
  --glass-blur-strong: var(--re-glass);
  --glass-blur-subtle: var(--re-glass-subtle);

  --glass-border: var(--re-border);
  --glass-border-light: var(--re-border-faint);
  --glass-border-hover: var(--re-border-strong);
  --glass-border-dark: var(--re-border);

  --glass-shadow: var(--re-shadow-1);
  --glass-shadow-hover: var(--re-shadow-hover);
  --glass-shadow-subtle: var(--re-shadow-1);
  --glass-shadow-inset:
    inset 0 2px 4px rgba(0, 0, 0, 0.3),
    inset 0 -1px 0 rgba(255, 255, 255, 0.12);

  --glass-noise-opacity: var(--re-noise-opacity);

  --radius-sm: var(--re-radius-sm);
  --radius-md: var(--re-radius-md);
  --radius-lg: var(--re-radius-lg);

  --transition-glass: all var(--re-transition);
}

/* ============================================
   LIGHT THEME

   Nothing to redefine here any more: the aliases above resolve through the
   --re-* tokens, and theme-tokens.css already shifts those for the light
   theme. The old hardcoded block here fought with that and was the reason
   panels looked like a different design between the two modes.
   ============================================ */
/* ============================================
   NOISE TEXTURE - SVG DATA URI
   ============================================ */

/* 
 * Inline SVG noise for performance - no external file needed.
 * Applied via ::before pseudo-element on glass containers.
 */

/* ============================================
   BASE GLASS CONTAINER
   ============================================ */

.glass,
.glass-container {
  background: var(--glass-bg) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--glass-border) !important;
  border-radius: var(--re-radius-lg);
  box-shadow: var(--glass-shadow) !important;
  position: relative;
  overflow: hidden;
  transition: var(--transition-glass);
}

/* Noise texture overlay */
.glass::before,
.glass-container::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  border-radius: inherit;
  z-index: 1;
}

/* Rim light / Glass highlight on top edge */
.glass::after,
.glass-container::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.3) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 2;
}

/* Content above effects */
.glass > *,
.glass-container > * {
  position: relative;
  z-index: 3;
}

/* Hover state */
.glass:hover,
.glass-container:hover {
  background: var(--glass-bg-hover) !important;
  backdrop-filter: var(--glass-blur-strong) !important;
  -webkit-backdrop-filter: var(--glass-blur-strong) !important;
  border-color: var(--glass-border-hover) !important;
  box-shadow: var(--glass-shadow-hover) !important;
}

/* ============================================
   GLASS VARIANTS
   ============================================ */

/* Light variant - more transparent */
.glass-light {
  background: var(--glass-bg-light) !important;
  border-color: var(--glass-border-light) !important;
}

/* Dark variant - more opaque with darker border */
.glass-dark {
  background: var(--glass-bg-dark) !important;
  border-color: var(--glass-border-dark) !important;
}

/* Static - no hover effects */
.glass-static:hover {
  background: var(--glass-bg) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border-color: var(--glass-border) !important;
  box-shadow: var(--glass-shadow) !important;
  transform: none;
}

/* Subtle - lighter effects */
.glass-subtle {
  backdrop-filter: var(--glass-blur-subtle) !important;
  -webkit-backdrop-filter: var(--glass-blur-subtle) !important;
  box-shadow: var(--glass-shadow-subtle) !important;
}

.glass-subtle::before {
  opacity: 0.03;
}

/* Inset/Indented variant - appears recessed */
.glass-inset {
  background: var(--re-surface) !important;
  box-shadow: var(--glass-shadow-inset) !important;
  border: 1px solid rgba(0, 0, 0, 0.3) !important;
}

/* ============================================
   NAVIGATION - GLASS STANDARD
   ============================================ */

.navbar {
  background: var(--re-surface) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border-bottom: 1px solid var(--re-border) !important;
  box-shadow: var(--glass-shadow) !important;
  position: relative;
}

/* Navbar noise texture */
.navbar::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseNav'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseNav)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 0;
}

/* Navbar rim light */
.navbar::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.2) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 1;
}

/* Navbar content above effects */
.navbar > * {
  position: relative;
  z-index: 2;
}

/* Nav items - NO glass, transparent */
.navbar .nav-item,
.navbar .nav-link {
  background: transparent !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* ============================================
   DROPDOWN MENUS - GLASS STANDARD + HIGH Z-INDEX
   ============================================ */

.dropdown-menu {
  background: var(--glass-bg-light) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--glass-border) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: var(--glass-shadow) !important;
  position: absolute;
  overflow: hidden;
  z-index: 99999999 !important; /* Maximum z-index to render above everything */
}

.dropdown-menu::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseDropdown'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseDropdown)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 0;
  border-radius: inherit;
}

.dropdown-menu::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.3) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 1;
}

.dropdown-menu > * {
  position: relative;
  z-index: 2;
}

/* Ensure dropdown shows above all containers */
.dropdown-menu.show {
  z-index: 99999999 !important;
}

/* ============================================
   HEADER CONTROLS PANEL - GLASS STANDARD
   ============================================ */

.header-controls-panel {
  background: var(--re-surface) !important;
  backdrop-filter: var(--re-glass) !important;
  -webkit-backdrop-filter: var(--re-glass) !important;
  border: 1px solid var(--re-border) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: 
    0 8px 32px 0 rgba(0, 0, 0, 0.5),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.18),
    inset 0 -1px 0 0 rgba(0, 0, 0, 0.15) !important;
  position: relative;
}

.header-controls-panel::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseControls'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseControls)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 0;
  border-radius: inherit;
}

.header-controls-panel::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.3) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 1;
}

.header-controls-panel > * {
  position: relative;
  z-index: 2;
}

/* ============================================
   FOOTER - GLASS STANDARD
   ============================================ */

footer,
.footer {
  background: var(--re-surface) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border-top: 1px solid var(--re-border) !important;
  box-shadow: 0 -8px 32px 0 rgba(0, 0, 0, 0.5),
              inset 0 1px 0 0 rgba(255, 255, 255, 0.18) !important;
  position: relative;
}

footer::before,
.footer::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFooter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFooter)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 0;
}

footer::after,
.footer::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.3) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 1;
}

footer > *,
.footer > * {
  position: relative;
  z-index: 2;
}

/* ============================================
   BREADCRUMBS - DARK TRANSPARENT GLASS
   Matches music player, navbar, header controls
   ============================================ */

.breadcrumb {
  background: var(--re-surface) !important;
  backdrop-filter: var(--re-glass) !important;
  -webkit-backdrop-filter: var(--re-glass) !important;
  border: 1px solid var(--re-border) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: 
    0 4px 16px 0 rgba(0, 0, 0, 0.4),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.18),
    inset 0 -1px 0 0 rgba(0, 0, 0, 0.15) !important;
  position: relative;
  overflow: hidden;
}

/* Light theme breadcrumb - frosted white glass */
[data-bs-theme="light"] .breadcrumb {
  background: rgba(255, 255, 255, 0.45) !important;
  border: 1px solid var(--re-border) !important;
  box-shadow: 
    0 4px 16px 0 rgba(0, 0, 0, 0.1),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.7),
    inset 0 -1px 0 0 rgba(0, 0, 0, 0.05) !important;
}

/* Noise texture overlay */
.breadcrumb::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseBreadcrumb'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseBreadcrumb)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 0;
  border-radius: inherit;
}

/* Top rim light - glass highlight */
.breadcrumb::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.2) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 1;
}

/* Light theme rim light - brighter */
[data-bs-theme="light"] .breadcrumb::after {
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.5) 50%, 
    transparent 100%
  );
}

.breadcrumb > * {
  position: relative;
  z-index: 2;
}

/* ============================================
   CARDS - GLASS STANDARD
   ============================================ */

.card,
.project-card,
.article-card,
.game-card {
  background: var(--glass-bg) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--glass-border) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow) !important;
  position: relative;
  overflow: hidden;
  transition: var(--transition-glass);
}

.card::before,
.project-card::before,
.article-card::before,
.game-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseCard'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseCard)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

.card::after,
.project-card::after,
.article-card::after,
.game-card::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.3) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 2;
}

.card > *,
.project-card > *,
.article-card > *,
.game-card > * {
  position: relative;
  z-index: 3;
}

/* Card hover */
.card:hover,
.project-card:hover,
.article-card:hover,
.game-card:hover {
  background: var(--glass-bg-hover) !important;
  backdrop-filter: var(--glass-blur-strong) !important;
  -webkit-backdrop-filter: var(--glass-blur-strong) !important;
  border-color: var(--glass-border-hover) !important;
  box-shadow: var(--glass-shadow-hover) !important;
  transform: translateY(-4px);
}

/* ============================================
   CONTENT SECTIONS - GLASS STANDARD
   ============================================ */

.about-section.contained,
.section-container,
.content-panel {
  background: var(--glass-bg) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--glass-border) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow) !important;
  position: relative;
  overflow: hidden;
  transition: var(--transition-glass);
}

/*
 * .about-section.open and .page-header were missing from these two lists, so
 * they were the only glass panels without the noise texture and rim light.
 * That went unnoticed while the 4px edge strips occupied the same ::before
 * slot; with those removed the gap showed. Both stripes of the alternating
 * section now match, and the page header matches the panels around it.
 */
.about-section.contained::before,
.about-section.open::before,
.page-header::before,
.article-card.contained::before,
.article-card.open::before,
.section-container::before,
.content-panel::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseSection'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseSection)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

.about-section.contained::after,
.about-section.open::after,
.page-header::after,
.article-card.contained::after,
.article-card.open::after,
.section-container::after,
.content-panel::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.3) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 2;
}

.about-section.contained > *,
.section-container > *,
.content-panel > * {
  position: relative;
  z-index: 3;
}

/* Section hover */
.about-section.contained:hover,
.section-container:hover {
  background: var(--glass-bg-hover) !important;
  backdrop-filter: var(--glass-blur-strong) !important;
  -webkit-backdrop-filter: var(--glass-blur-strong) !important;
  border-color: var(--glass-border-hover) !important;
  box-shadow: var(--glass-shadow-hover) !important;
  transform: translateY(-4px);
}

/* ============================================
   PAGE CONTAINERS - GLASS STANDARD
   Main content containers that wrap page content
   ============================================ */

.page-container,
.container.glass,
.container.py-4.py-xl-5[style*="border-radius"] {
  background: var(--glass-bg) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--glass-border) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow) !important;
  position: relative;
  overflow: hidden;
}

.page-container::before,
.container.glass::before,
.container.py-4.py-xl-5[style*="border-radius"]::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noisePage'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noisePage)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

.page-container::after,
.container.glass::after,
.container.py-4.py-xl-5[style*="border-radius"]::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.3) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 2;
}

.page-container > *,
.container.glass > *,
.container.py-4.py-xl-5[style*="border-radius"] > * {
  position: relative;
  z-index: 3;
}

/* ============================================
   ADMIN PANELS - GLASS STANDARD
   ============================================ */

.admin-content-panel,
.admin-filters-panel,
.admin-panel {
  background: var(--glass-bg) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--glass-border) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow) !important;
  position: relative;
  overflow: hidden;
}

.admin-content-panel::before,
.admin-filters-panel::before,
.admin-panel::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseAdmin'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseAdmin)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

.admin-content-panel::after,
.admin-filters-panel::after,
.admin-panel::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.3) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 2;
}

.admin-content-panel > *,
.admin-filters-panel > *,
.admin-panel > * {
  position: relative;
  z-index: 3;
}

/* ============================================
   MODALS - GLASS STANDARD
   ============================================ */

.modal-content {
  background: var(--glass-bg) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--glass-border) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow) !important;
  position: relative;
  overflow: hidden;
}

.modal-content::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseModal'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseModal)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

.modal-content::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.3) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 2;
}

.modal-content > * {
  position: relative;
  z-index: 3;
}

/* ============================================
   FORM INPUTS - GLASS STANDARD
   ============================================ */

.form-control,
.form-select,
.admin-input {
  background: var(--re-surface) !important;
  backdrop-filter: var(--re-glass-subtle) !important;
  -webkit-backdrop-filter: var(--re-glass-subtle) !important;
  border: 1px solid var(--glass-border) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3) !important;
  color: var(--re-text-color) !important;
  transition: var(--transition-glass);
}

.form-control:focus,
.form-select:focus,
.admin-input:focus {
  background: var(--glass-bg-light) !important;
  backdrop-filter: var(--glass-blur-subtle) !important;
  -webkit-backdrop-filter: var(--glass-blur-subtle) !important;
  border-color: var(--re-brand-400) !important;
  box-shadow: 0 0 0 3px rgba(130, 107, 125, 0.25),
              inset 0 1px 2px rgba(0, 0, 0, 0.2) !important;
  outline: none;
}

.form-control::placeholder {
  color: rgba(238, 224, 218, 0.5);
}

/* ============================================
   ALERTS - GLASS STANDARD
   ============================================ */

.alert {
  background: var(--glass-bg) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--glass-border) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: var(--glass-shadow-subtle) !important;
  position: relative;
  overflow: hidden;
}

.alert::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseAlert'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseAlert)'/%3E%3C/svg%3E");
  opacity: 0.04;
  pointer-events: none;
  z-index: 0;
  border-radius: inherit;
}

.alert > * {
  position: relative;
  z-index: 1;
}

/* ============================================
   ARTICLE CONTAINERS - ALTERNATING GLASS
   ============================================ */

.article-container-dark {
  background: var(--re-surface) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--re-border) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow) !important;
  position: relative;
  overflow: hidden;
}

.article-container-light {
  background: var(--re-surface-raised) !important;
  backdrop-filter: var(--glass-blur) !important;
  -webkit-backdrop-filter: var(--glass-blur) !important;
  border: 1px solid var(--re-border) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow) !important;
  position: relative;
  overflow: hidden;
}

.article-container-dark::before,
.article-container-light::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseArticle'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseArticle)'/%3E%3C/svg%3E");
  opacity: var(--glass-noise-opacity);
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

.article-container-dark::after,
.article-container-light::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, 
    transparent 0%, 
    rgba(255, 255, 255, 0.25) 50%, 
    transparent 100%
  );
  pointer-events: none;
  z-index: 2;
}

.article-container-dark > *,
.article-container-light > * {
  position: relative;
  z-index: 3;
}

/* ============================================
   EXCEPTIONS - NO GLASS
   ============================================ */

/* Music header - NO glass */
.music-header,
.music-header * {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* Main wrapper containers - NO glass */
body > .container,
.container[style*="padding-left: 0"],
.container[style*="margin-bottom: 90px"]:not(.glass) {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  background: transparent !important;
}

/* Skills section - NO outer glass */
[data-section-key="skills"].about-section.contained {
  background: transparent !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  box-shadow: none !important;
  border: none !important;
}

[data-section-key="skills"].about-section.contained::before,
[data-section-key="skills"].about-section.contained::after {
  display: none !important;
}

/* ============================================
   RESPONSIVE OPTIMIZATIONS
   ============================================ */

@media (max-width: 768px) {
  :root {
    --glass-blur: blur(12px) saturate(160%) contrast(105%);
    --glass-blur-strong: blur(16px) saturate(180%) contrast(110%);
    --glass-noise-opacity: 0.03;
  }
  
  /* Simpler shadows on mobile */
  .glass,
  .glass-container,
  .card,
  .project-card,
  .about-section.contained {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4),
                inset 0 1px 0 rgba(255, 255, 255, 0.1) !important;
  }
}

/* ============================================
   REDUCED MOTION
   ============================================ */

@media (prefers-reduced-motion: reduce) {
  .glass,
  .glass-container,
  .card,
  .project-card,
  .about-section.contained {
    transition: none !important;
  }
  
  .glass:hover,
  .glass-container:hover,
  .card:hover,
  .project-card:hover,
  .about-section.contained:hover {
    transform: none !important;
  }
}

/* ============================================
   HIGH CONTRAST MODE
   ============================================ */

@media (prefers-contrast: high) {
  :root {
    --glass-border: rgba(255, 255, 255, 0.5);
    --glass-border-hover: rgba(155, 89, 182, 0.8);
  }
  
  .glass::after,
  .glass-container::after,
  .card::after,
  .navbar::after,
  footer::after {
    background: linear-gradient(90deg, 
      transparent 0%, 
      rgba(255, 255, 255, 0.5) 50%, 
      transparent 100%
    );
  }
}

/* ============================================
   PRINT STYLES
   ============================================ */

@media print {
  .glass,
  .glass-container,
  .card,
  .project-card,
  .navbar,
  footer,
  .modal-content {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    background: white !important;
    border: 1px solid #ccc !important;
    box-shadow: none !important;
  }
  
  .glass::before,
  .glass-container::before,
  .card::before,
  .navbar::before,
  footer::before {
    display: none !important;
  }
  
  .glass::after,
  .glass-container::after,
  .card::after,
  .navbar::after,
  footer::after {
    display: none !important;
  }
}


/* ===== assets/css/Silkscreen.css ===== */
/* Preemptively import from high-reliability, optimized Google Fonts CDN */


/* Fallback local font-face in case of offline usage or CDN blockages */
@font-face {
	font-family: 'Silkscreen';
	src: url(../fonts/slkscr.eot) format('embedded-opentype'),
	url(../fonts/slkscr.eot) format('embedded-opentype'),
	url(../fonts/slkscr.woff2) format('woff2'),
	url(../fonts/slkscr.ttf) format('truetype');
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

/* ThaleahFat Retro Pixel Art Web Font */
@font-face {
	font-family: 'ThaleahFat';
	src: url(../fonts/ThaleahFat.ttf) format('truetype');
	font-weight: normal;
	font-style: normal;
	font-display: swap;
}

/* ===== assets/css/silkscreen-enforce-6px.css ===== */
/**
 * Silkscreen Font Size Enforcement
 * Ensures Silkscreen font is set to 8px and enforces pixel-perfect rendering
 * 
 * Silkscreen is a pixel font designed for small sizes. 8px provides the best balance
 * between readability and maintaining the pixel-perfect aesthetic.
 */

/* ==============================================================================
   SITE TITLE/BRANDING - ALBERT SANS WITH MAXIMUM SPECIFICITY
   ============================================================================== */

/* NUCLEAR OPTION: Force Albert Sans on navbar brand with EXTREME specificity */
nav.navbar .navbar-brand,
nav.navbar a.navbar-brand,
.navbar.navbar-dark .navbar-brand,
.navbar.navbar-expand-md .navbar-brand,
nav.navbar .navbar-brand *,
nav.navbar .navbar-brand span,
/*
 * BRAND WORDMARK - font-family deliberately NOT set here.
 *
 * This block used to force the brand and every child of it to the body face,
 * using selectors as specific as `nav.navbar a.navbar-brand span` (0,2,3).
 * theme-color-fix.css sets the opposite - the display face, under a heading
 * "Apply ThaleahFat to the Main Website Logo / Navbar Branding" - at (0,1,1),
 * so it lost regardless of load order and the wordmark rendered in body text
 * while the rest of the site's identity used the pixel face.
 *
 * Two files asserting opposite things is the bug. The face is now decided in
 * one place (theme-color-fix.css); this block keeps only the metrics.
 */
nav.navbar a.navbar-brand span,
.navbar .navbar-brand[class],
.navbar .navbar-brand[style],
nav .navbar-brand,
.fixed-top .navbar-brand,
header .navbar-brand,
.navbar-brand {
    font-weight: inherit !important;
    letter-spacing: normal !important;
}

/* Force navigation links to Albert Sans */
/*
 * The navigation uses the UI face.
 *
 * This file loads after navigation-final-fix.css, so this block is what
 * actually decides the face - the other one only agrees with it. Both were
 * changed together rather than adding a third override, so neither file is
 * left asserting something untrue.
 */
.navbar .nav-link,
.navbar .dropdown-toggle,
.navbar-nav .nav-link,
nav .nav-link,
.navbar-nav a {
    font-family: var(--re-font-ui) !important;
}

/* ==============================================================================
   PAGINATION BUTTONS - MUST USE SILKSCREEN 8px
   ============================================================================== */

/* Force Silkscreen 8px on ALL pagination buttons */
.pagination a,
.pagination span,
.pagination-controls a,
.pagination-controls span,
[href*="visitors_page"],
div[style*="pagination"] a,
div[style*="pagination"] span,
a[href*="page=admin"][href*="visitors_page"] {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    -webkit-font-smoothing: none !important;
    -moz-osx-font-smoothing: grayscale !important;
    text-rendering: optimizeSpeed !important;
}

/* Admin dashboard pagination page numbers - ALL must be Silkscreen 8px */
.admin-dashboard .pagination a,
body[data-page*="admin"] .pagination a,
body[data-page*="admin"] .pagination span,
body[data-page*="admin"] [style*="pagination"] a,
body[data-page*="admin"] [style*="pagination"] span,
body[data-page*="admin"] a[style*="Albert Sans"],
body[data-page*="admin"] span[style*="Albert Sans"] {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
}

/* Target inline styled pagination links with Albert Sans - OVERRIDE */
body[data-page="admin"] a[style*="font-family: 'Albert Sans'"],
body[data-page="admin"] span[style*="font-family: 'Albert Sans'"] {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
}

/* Specific targeting for visitor page pagination */
a[href*="visitors_page"][style*="Albert Sans"],
span[style*="Albert Sans"][style*="min-width: 40px"] {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
}

/* ==============================================================================
   SILKSCREEN FONT SIZE ENFORCEMENT - OPTIMAL SIZE
   Forces ALL Silkscreen usage to 8px for best readability
   ============================================================================== */

/* Target all elements using Silkscreen font */
[style*="font-family: 'Silkscreen'"],
[style*="font-family: \"Silkscreen\""],
[style*="font-family:Silkscreen"],
.silkscreen,
*[class*="silkscreen"] {
    /* Optimal size for pixel-perfect readability */
    font-size: 8px !important;
    
    /* Pixel-perfect rendering */
    -webkit-font-smoothing: none !important;
    -moz-osx-font-smoothing: grayscale !important;
    font-smooth: never !important;
    text-rendering: optimizeSpeed !important;
  
    /* Prevent scaling */
    transform: scale(1) !important;
    zoom: 1 !important;
    
    /* Lock line height for pixel perfection */
    line-height: 1.2 !important;
    
    /* Prevent letter spacing changes */
    letter-spacing: 0 !important;
}

/* ==============================================================================
   SPECIFIC OVERRIDES - Enforce 8px everywhere Silkscreen appears
   ============================================================================== */

/* Breadcrumbs */
.breadcrumb [style*="Silkscreen"],
.breadcrumb * {
    font-size: 8px !important;
}

/* Navigation Dropdowns - ONLY dropdown items */
.dropdown-menu .dropdown-item[style*="Silkscreen"],
.dropdown-menu [style*="Silkscreen"] {
    font-size: 8px !important;
}

/* Article Meta */
.article-meta-item,
.article-category,
.article-featured-badge,
.article-read-more,
.article-views {
    font-size: 8px !important;
}

/* Admin Panel */
.admin-panel [style*="Silkscreen"],
.admin-sidebar [style*="Silkscreen"],
.admin-header [style*="Silkscreen"] {
    font-size: 8px !important;
}

/* Form Labels */
label [style*="Silkscreen"],
.form-label [style*="Silkscreen"] {
    font-size: 8px !important;
}

/*
 * Footer - all Silkscreen 8px, EXCEPT .site-footer.
 *
 * The universal selector here was written when the footer was a single line
 * reading "(c) 2025 realityengine", where 8px pixel type is exactly right.
 * The rebuilt footer (see site-chrome.css) carries a name, a role, seven
 * links and a status line - things meant to be read - and this rule flattened
 * every one of them to 8px Silkscreen regardless of what that file asked for,
 * because "footer *" beats any single-class selector and carries !important
 * besides.
 *
 * It still applies to any other footer. The one exclusion is the site footer,
 * which sets its own type: Silkscreen for small caps labels only, and Albert
 * Sans for anything that is a sentence.
 */
footer:not(.server-info-footer) [style*="Silkscreen"],
footer:not(.server-info-footer) .footer [style*="Silkscreen"],
footer:not(.site-footer):not(.server-info-footer) *,
.footer:not(.site-footer):not(.server-info-footer) * {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    -webkit-font-smoothing: none !important;
    -moz-osx-font-smoothing: grayscale !important;
}

/* Header */
header [style*="Silkscreen"],
.header [style*="Silkscreen"] {
    font-size: 8px !important;
}

/* Small text / helper text */
small [style*="Silkscreen"],
.small [style*="Silkscreen"] {
    font-size: 8px !important;
}

/* Badges */
.badge [style*="Silkscreen"],
span.badge [style*="Silkscreen"] {
    font-size: 8px !important;
}

/* Nav Tabs - Prevent oversizing */
.nav-tabs .nav-link {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* Nav Tabs with Silkscreen (if any) */
.nav-tabs [style*="Silkscreen"] {
    font-size: 8px !important;
}

/* ==============================================================================
   ADMIN DASHBOARD - FORCE SPECIFIC BUTTONS TO 8PX SILKSCREEN (NOT ALL)
   ============================================================================== */

/* Target calibration button specifically */
#calibrationToggleBtn,
button[onclick*="toggleCalibrationMode"],
.map-calibration-btn {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
}

/* Auto-Resolve All button */
#autoResolveBtn,
button[onclick*="autoResolveAll"] {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
}

/* Bot filter toggle button */
.bot-filter-btn,
button[onclick*="toggleBotFilter"] {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
}

/* Resolve IP buttons */
.resolve-ip-btn,
button[class*="resolve-ip"] {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
}

/* ============================================
   ADMIN DASHBOARD SPECIFIC
   ============================================ */

/* Admin header buttons (Settings, Diagnostics) */
.btn[href*="section=settings"],
.btn[href*="section=diagnostics"],
.admin-header .btn,
.admin-page-header .btn {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    padding: 8px 18px !important;
    -webkit-font-smoothing: none !important;
    -moz-osx-font-smoothing: grayscale !important;
    text-rendering: optimizeSpeed !important;
}

/* Force Silkscreen on ALL admin buttons with inline styles */
[style*="padding: 10px 20px"] {
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    -webkit-font-smoothing: none !important;
    -moz-osx-font-smoothing: grayscale !important;
    text-rendering: optimizeSpeed !important;
}

/* ==============================================================================
   PIXEL PERFECTION SETTINGS
   ============================================================================== */

@font-face {
    font-family: 'Silkscreen';
    font-display: block;
    size-adjust: 100%;
}

/* Force pixel grid alignment */
* {
    -webkit-text-size-adjust: 100% !important;
    -ms-text-size-adjust: 100% !important;
}

/* Prevent browser zoom from affecting pixel fonts */
@media (min-resolution: 192dpi) {
    [style*="font-family: 'Silkscreen'"],
 [style*="font-family: \"Silkscreen\""] {
 font-size: 8px !important;
        transform: scale(1) !important;
    }
}

/* ==============================================================================
   RESPONSIVE - Maintain 8px across all breakpoints
   ============================================================================== */

/* Desktop */
@media (min-width: 1200px) {
    [style*="Silkscreen"] {
        font-size: 8px !important;
    }
}

/* Tablet */
@media (min-width: 768px) and (max-width: 1199px) {
    [style*="Silkscreen"] {
        font-size: 8px !important;
    }
}

/* Mobile */
@media (max-width: 767px) {
  [style*="Silkscreen"] {
        font-size: 8px !important;
    }
}

/* Small mobile */
@media (max-width: 480px) {
    [style*="Silkscreen"] {
 font-size: 8px !important;
  }
}

/* ==============================================================================
PRINT STYLES - Maintain pixel perfection in print
   ============================================================================== */

@media print {
    [style*="Silkscreen"] {
      font-size: 8pt !important; /* Use pt for print */
  -webkit-print-color-adjust: exact !important;
 print-color-adjust: exact !important;
    }
}

/* ==============================================================================
   ACCESSIBILITY - Ensure readability at 8px
   ============================================================================== */

/* High contrast mode support */
@media (prefers-contrast: high) {
    [style*="Silkscreen"] {
        font-size: 8px !important;
        font-weight: normal !important;
     -webkit-font-smoothing: none !important;
    }
}

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
    [style*="Silkscreen"] {
transition: none !important;
        animation: none !important;
    }
}

/* ==============================================================================
NOTES FOR DEVELOPERS
   ============================================================================== */

/*
 * WHY 8px FOR SILKSCREEN?
 * 
 * 1. PIXEL PERFECTION WITH READABILITY
 *    - 8px maintains crisp pixel edges
 *    - Better readability than smaller sizes
 *    - Still preserves retro aesthetic
 * 
 * 2. OPTIMAL BALANCE
 *    - Small enough to be accent font
 *- Large enough to be easily readable
 *    - Maintains pixel grid alignment
 * 
 * 3. VISUAL HIERARCHY
 *    - Silkscreen at 8px = labels, meta, badges
 *    - Albert Sans at 14-48px = content, headers
 *    - Clear distinction maintained
 * 
 * 4. CONSISTENCY
 *  - Single size across entire site
 *    - Professional appearance
 *    - Easy maintenance
 */


/* ===== assets/css/japanese-support.css ===== */
/**
 * Japanese Character Support
 * Ensures proper rendering of Japanese text (???)
 */

/* Global Japanese Font Support - BUT preserve Silkscreen where specified */
body:not(.silkscreen),
html:not(.silkscreen),
h1:not(.silkscreen):not([style*="Silkscreen"]),
h2:not(.silkscreen):not([style*="Silkscreen"]),
h3:not(.silkscreen):not([style*="Silkscreen"]),
h4:not(.silkscreen):not([style*="Silkscreen"]),
h5:not(.silkscreen):not([style*="Silkscreen"]),
h6:not(.silkscreen):not([style*="Silkscreen"]),
p:not(.silkscreen):not([style*="Silkscreen"]),
div:not(.silkscreen):not([style*="Silkscreen"]),
a:not(.silkscreen):not([style*="Silkscreen"]),
li:not(.silkscreen):not([style*="Silkscreen"]) {
    -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

/* PRESERVE Silkscreen font for pixel-perfect elements */
.breadcrumb,
.breadcrumb *,
.breadcrumb-item,
.breadcrumb-item *,
button,
.btn,
.btn *,
a.btn,
a.btn *,
input[type="submit"],
input[type="button"],
.article-read-more,
.article-read-more *,
.nav-link,
.dropdown-item,
[style*="Silkscreen"],
[style*="font-family: Silkscreen"],
[style*="font-family:Silkscreen"],
span[style*="font-family: Silkscreen"],
span[style*="font-family:Silkscreen"] {
    font-family: 'Silkscreen', 'Noto Sans JP', serif !important;
}

/* Specific support for Japanese in Silkscreen fallback */
.silkscreen-jp {
    font-family: 'Silkscreen', 'Noto Sans JP', monospace !important;
}

/* Ensure proper line-height for Japanese characters */
.jp-text,
[lang="ja"],
[lang="jp"] {
    line-height: 1.8 !important;
    letter-spacing: 0.05em !important;
}

/* Article content with Japanese support */
.article-content,
.article-excerpt,
.post-content {
    font-family: var(--re-font-body) !important;
}

/* Titles use Albert Sans */
.article-title,
h1, h2, h3, h4, h5, h6 {
    font-family: var(--re-font-body) !important;
}

/* Navbar brand with Japanese support (but preserve Silkscreen in dropdowns) */
.navbar-brand {
    font-family: var(--re-font-body) !important;
}

/* Form inputs with Japanese support */
input[type="text"],
input[type="email"],
textarea,
select {
    font-family: var(--re-font-body) !important;
}

/* Music track titles with Japanese support */
.track-item:not([style*="Silkscreen"]),
.playlist-item:not([style*="Silkscreen"]) {
    font-family: var(--re-font-body) !important;
}

/* Ensure proper vertical alignment for mixed Latin/Japanese text */
.mixed-text {
    vertical-align: baseline !important;
}


/* ===== assets/css/header-breadcrumb-spacing.css ===== */
/**
 * Header to Breadcrumb Consistent Spacing
 * Ensures uniform spacing between header controls and breadcrumb across all pages
 */

/*
 * Theme Toggle & User Info Container - clearance under the fixed navbar.
 *
 * 64px was a guess at the navbar's height and it was wrong at every
 * breakpoint: the bar is 77px on a desktop, 73 on a phone and 70 at the
 * tablet size, so the strip sat UNDER it by 5px, 5px and 2px respectively.
 *
 * --re-nav-height is measured from the navbar itself by
 * assets/js/nav-offset.js, so this tracks the real height instead of
 * guessing, and the 24px is the gap we actually want to see. The 77px
 * fallback keeps the desktop case right with JavaScript off.
 *
 * The selector still matches on the inline style attribute, which is not
 * something to copy: global-glass-refraction.css keys off the same string,
 * so the inline padding-top in templates/header.php cannot be removed
 * without breaking that file too. It is a hook now, not a value - this rule
 * overrides whatever it says.
 */
body > .container[style*="padding-top: 64px"] {
    padding-top: calc(var(--re-nav-height, 77px) + 24px) !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    margin-bottom: 0 !important;
}

/* Breadcrumb Container - Consistent spacing */
.container.effect[style*="padding-bottom: 72px"],
.container[style*="padding-top: 0"][style*="padding-bottom: 72px"] {
    padding-left: 0 !important;
    padding-right: 0 !important;
    padding-top: 16px !important; /* Consistent 16px gap from header controls */
    padding-bottom: 72px !important;
    margin-bottom: 0 !important;
}

/* Breadcrumb element itself */
.breadcrumb {
    margin-top: 0 !important; /* Remove margin-top since container handles spacing */
}

/* Main content container after breadcrumb */
body > .container[style*="margin-bottom: 90px"] {
    padding-left: 0 !important;
    padding-right: 0 !important;
}

/* Ensure consistent spacing for pages without breadcrumb component */
body > .container:not([style*="padding-top: 64px"]):first-of-type {
    margin-top: 80px; /* 64px header + 16px spacing */
}

/* Mobile adjustments */
@media (max-width: 768px) {
    /* Same measured clearance as the desktop rule above, with a slightly
       tighter gap: vertical space is scarcer on a phone. 60px here was under
       the mobile navbar's real 73px, which is why the strip overlapped at
       every width and not just the wide ones. */
    body > .container[style*="padding-top: 64px"] {
        padding-top: calc(var(--re-nav-height, 73px) + 16px) !important;
    }
    
    .container.effect[style*="padding-bottom: 72px"],
    .container[style*="padding-top: 0"][style*="padding-bottom: 72px"] {
        padding-top: 12px !important;
        padding-bottom: 48px !important;
    }
}


/* ===== assets/css/breadcrumb-standard.css ===== */
/**
 * Standardized Breadcrumb Component
 * Consistent pulsing dot and styling across all pages
 * Uses DARK TRANSPARENT GLASS matching music player
 */

/* Breadcrumb Container - Dark Transparent Glass */
.breadcrumb {
    padding: 8px 16px !important;
    margin-top: 0 !important;
    font-size: 8px !important;
    font-family: var(--re-font-ui) !important;
    display: flex !important;
    align-items: center !important;
    list-style: none !important;
    border-radius: var(--re-radius-md) !important;
    
    /* Dark transparent glass - matches music player */
    background: var(--re-surface) !important;
    backdrop-filter: var(--re-glass) !important;
    -webkit-backdrop-filter: var(--re-glass) !important;
    
    /* Purple-tinted border */
    border: 1px solid var(--re-border) !important;
    
    /* Multi-layer shadow with rim light */
    box-shadow: 
        0 4px 16px 0 rgba(0, 0, 0, 0.4),
        inset 0 1px 0 0 rgba(255, 255, 255, 0.18),
        inset 0 -1px 0 0 rgba(0, 0, 0, 0.15) !important;
    
    position: relative;
    overflow: hidden;
}

/* Light theme - frosted white glass */
[data-bs-theme="light"] .breadcrumb {
    background: rgba(255, 255, 255, 0.45) !important;
    border: 1px solid var(--re-border) !important;
    box-shadow: 
        0 4px 16px 0 rgba(0, 0, 0, 0.1),
        inset 0 1px 0 0 rgba(255, 255, 255, 0.7),
        inset 0 -1px 0 0 rgba(0, 0, 0, 0.05) !important;
}

/* Noise texture overlay */
.breadcrumb::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseBreadcrumb'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseBreadcrumb)'/%3E%3C/svg%3E");
    opacity: 0.07;
    pointer-events: none;
    z-index: 0;
    border-radius: inherit;
}

/* Top rim light - glass highlight */
.breadcrumb::after {
    content: '';
    position: absolute;
    top: 1px;
    left: 8px;
    right: 8px;
    height: 1px;
    background: linear-gradient(90deg, 
        transparent 0%, 
        rgba(255, 255, 255, 0.2) 50%, 
        transparent 100%
    );
    pointer-events: none;
    z-index: 1;
}

/* Light theme rim light - brighter */
[data-bs-theme="light"] .breadcrumb::after {
    background: linear-gradient(90deg, 
        transparent 0%, 
        rgba(255, 255, 255, 0.5) 50%, 
        transparent 100%
    );
}

/* Content above effects */
.breadcrumb > * {
    position: relative;
    z-index: 2;
}

/* Breadcrumb Items */
.breadcrumb-item {
    display: flex !important;
    align-items: center !important;
    line-height: 1 !important;
}

.breadcrumb-item + .breadcrumb-item {
    margin-left: 8px !important;
}

/* Separator - using slash not ::before */
.breadcrumb-item + .breadcrumb-item::before {
    content: "/" !important;
    color: rgba(130, 107, 125, 0.5) !important;
    padding-right: 8px !important;
}

/* Breadcrumb Links */
.breadcrumb-item a {
    color: var(--re-text-color, rgba(255, 255, 255, 0.92)) !important;
    text-decoration: none !important;
    transition: opacity 0.2s ease !important;
}

.breadcrumb-item a:hover {
    opacity: 0.8 !important;
    color: var(--bs-link-hover-color) !important;
}

/* Active Breadcrumb Item */
.breadcrumb-item.active {
    color: var(--re-brand-600) !important;
    display: flex !important;
    align-items: center !important;
}

/* Pulsing Circle Icon */
.breadcrumb-item.active .bi-circle-fill,
.breadcrumb-item.active svg.bi-circle-fill {
    width: 6px !important;
    height: 6px !important;
    color: var(--bs-form-invalid-border-color) !important;
    margin: 0 6px 0 0 !important;
    flex-shrink: 0 !important;
    animation: breadcrumb-pulse 2s ease-in-out infinite !important;
}

/* Active Item Text */
.breadcrumb-item.active span {
    color: var(--re-text-color, rgba(255, 255, 255, 0.92)) !important;
    line-height: 1 !important;
}

/* Pulse Animation */
@keyframes breadcrumb-pulse {
    0%, 100% {
        opacity: 1;
        transform: scale(1);
    }
    50% {
        opacity: 0.5;
        transform: scale(0.9);
    }
}

/* Flash Animation Class (Bootstrap Animate.css style) */
.flash {
    animation: breadcrumb-pulse 2s ease-in-out infinite !important;
}

.animated {
    animation-duration: 2s !important;
    animation-fill-mode: both !important;
}

.infinite {
    animation-iteration-count: infinite !important;
}

/* Mobile Responsiveness */
@media (max-width: 768px) {
    .breadcrumb {
        font-size: 7px !important;
        padding: 6px 12px !important;
        backdrop-filter: blur(12px) saturate(160%) contrast(105%) !important;
        -webkit-backdrop-filter: blur(12px) saturate(160%) contrast(105%) !important;
    }
    
    .breadcrumb::before {
        opacity: 0.03;
    }
    
    .breadcrumb-item + .breadcrumb-item {
        margin-left: 6px !important;
    }
    
    .breadcrumb-item.active .bi-circle-fill {
        margin-right: 4px !important;
    }
}


/* ===== assets/css/header-controls-panel.css ===== */
/**
 * Header Controls Panel - Mobile-Friendly Redesign
 * Unified panel for theme toggle, user profile, and language selector
 * Uses GLASS STANDARD - matches music player
 */

/* ============================================
   FOREGROUND TOKENS

   The panel's glass is dark in BOTH themes - glass-standard.css paints it
   rgba(20, 15, 22, 0.6) regardless of data-bs-theme. Its text was set to
   var(--bs-body-bg), which is white under the light theme but #212529 under the
   dark one, so switching to dark mode turned the whole panel into dark ink on
   dark glass. A panel whose background does not follow the theme must not take
   its foreground from the theme either.

   Declared on :root so they are also in scope for the language menu while it is
   portaled onto <body> - see the Z-ORDER NOTE further down.
   ============================================ */

:root {
    --header-controls-fg: #e8dfe6;
}

/* ============================================
   PANEL CONTAINER - GLASS STANDARD
   ============================================ */

.header-controls-panel {
    /* Layout */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
    
    /* Spacing */
    padding: 12px 20px;
    margin-top: 16px;
    margin-bottom: 16px;
    
    /* GLASS STANDARD */
    background: var(--re-surface);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-md);
    box-shadow: 
        0 8px 32px 0 rgba(0, 0, 0, 0.5),
        inset 0 1px 0 0 rgba(255, 255, 255, 0.15),
        inset 0 -1px 0 0 rgba(0, 0, 0, 0.15);
    
    /* Typography */
    font-family: var(--re-font-ui);
    font-size: 8px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--header-controls-fg) !important;
    
    /* Positioning - REMOVED overflow: hidden to allow dropdown to escape */
    position: relative;
    z-index: 100;
}

/* Noise texture overlay */
.header-controls-panel::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseHeaderControls'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseHeaderControls)'/%3E%3C/svg%3E");
    opacity: 0.06;
    pointer-events: none;
    z-index: 0;
    border-radius: inherit;
}

/* Rim light on top edge */
.header-controls-panel::after {
    content: '';
    position: absolute;
    top: 1px;
    left: 8px;
    right: 8px;
    height: 1px;
    background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
    pointer-events: none;
    z-index: 1;
}

/* Content above effects */
.header-controls-panel > * {
    position: relative;
    z-index: 2;
}

/* ============================================
   THEME TOGGLE SECTION
   ============================================ */

.header-controls-theme {
    display: flex;
    align-items: center;
    gap: 8px;
}

.header-controls-theme a {
    color: var(--header-controls-fg) !important;
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 8px;
    transition: all 0.3s ease;
}

.header-controls-theme a:hover {
    color: var(--bs-link-hover-color) !important;
    transform: translateY(-1px);
}

.header-controls-theme svg {
    color: var(--bs-link-hover-color) !important;
    font-size: 26px;
    transition: transform 0.3s ease;
}

.header-controls-theme a:hover svg {
    transform: scale(1.1);
}

/* ============================================
   USER PROFILE SECTION
   ============================================ */

.header-controls-user {
    display: flex;
    align-items: center;
    gap: 15px;
    flex-wrap: wrap;
}

/* Admin Icon */
.header-controls-admin-icon {
    color: #ff6b6b;
    text-decoration: none;
    display: flex;
    align-items: center;
    transition: all 0.3s ease;
}

.header-controls-admin-icon svg {
    font-size: 20px;
}

.header-controls-admin-icon:hover {
    color: #ff5252;
    transform: scale(1.1) rotate(90deg);
}

/* Profile Link */
.header-controls-profile {
    color: var(--header-controls-fg) !important;
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 8px;
    transition: all 0.3s ease;
}

.header-controls-profile:hover {
    color: var(--bs-link-hover-color) !important;
    transform: translateY(-1px);
}

.header-controls-profile svg {
    color: var(--bs-link-hover-color) !important;
    font-size: 22px;
    transition: transform 0.3s ease;
}

.header-controls-profile:hover svg {
    transform: scale(1.1);
}

/* User Role Display */
.user-role-display {
    opacity: 0.8;
}

/* Register Link */
.header-controls-register {
    color: var(--header-controls-fg) !important;
    text-decoration: none !important;
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important;
    text-transform: uppercase !important;
    letter-spacing: 1px !important;
    display: inline-flex !important;
    align-items: center !important;
    transition: color 0.3s ease !important;
    white-space: nowrap !important;
    line-height: 1 !important;
    padding: 0 !important;
    background: none !important;
    border: none !important;
}

.header-controls-register:hover {
    color: var(--bs-link-hover-color) !important;
}
/* ============================================
   LANGUAGE / REGION SELECTOR

   Z-ORDER NOTE - read before touching any number in here.

   .header-controls-panel creates a stacking context: it has `backdrop-filter`
   AND `position: relative; z-index: 100`. Either one alone is enough. That means
   every z-index set on this menu is resolved *inside the panel*, so the menu was
   painted as though it were at z-index 100 - underneath the fixed footer (9997),
   the server-info bar (9996) and the music player (9999) - no matter how many
   nines were stacked on it. Raising the number again will not help.

   assets/js/language-dropdown-fix.js re-parents the menu to <body> while it is
   open, which takes it out of the panel's stacking context for real. The
   `.header-controls-lang-portal` rules below style that portaled state; the menu
   is anchored to the toggle in viewport coordinates by that script.
   ============================================ */

.header-controls-lang,
span.dropdown.header-controls-lang {
    position: relative;
}
/* The toggle button */
.header-controls-lang-toggle {
    color: var(--header-controls-fg) !important;
    text-decoration: none;
    padding: 6px 12px;
    background: var(--re-surface-raised);
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-sm);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: all 0.3s ease;
    cursor: pointer;
}

.header-controls-lang-toggle:hover {
    background: var(--re-surface-hover);
    border-color: var(--re-border-strong);
    color: var(--bs-link-hover-color) !important;
    transform: translateY(-1px);
}

/* Menu chrome - applies both in place and while portaled to <body>.
   The portal class has to repeat the glass here because once the menu is moved
   the descendant selectors (.header-controls-lang .dropdown-menu) stop matching. */
.header-controls-lang .dropdown-menu,
.dropdown-menu.header-controls-lang-portal {
    /* GLASS STANDARD */
    background: rgba(20, 15, 22, 0.92) !important;
    backdrop-filter: var(--re-glass) !important;
    -webkit-backdrop-filter: var(--re-glass) !important;
    border: 1px solid var(--re-border) !important;
    border-radius: var(--re-radius-md) !important;
    box-shadow:
        0 12px 40px 0 rgba(0, 0, 0, 0.6),
        inset 0 1px 0 0 rgba(255, 255, 255, 0.18),
        inset 0 -1px 0 0 rgba(0, 0, 0, 0.15) !important;

    min-width: 140px !important;
    padding: 6px !important;
    overflow: hidden !important;
}

/* Portaled state: the menu is a direct child of <body>, so it is a sibling of the
   fixed footer and the music player and its z-index finally competes with theirs.
   `position: fixed` + the top/left written by language-dropdown-fix.js anchor it
   to the toggle. The `body >` prefix is what outranks the blanket
   `.dropdown-menu { position: absolute !important }` in z-index-layers.css. */
body > .dropdown-menu.header-controls-lang-portal {
    position: fixed !important;
    z-index: 10500 !important;
    right: auto !important;
    bottom: auto !important;
    margin: 0 !important;
}

/* Bootstrap sets .show; keep the portaled menu hidden until it does. */
body > .dropdown-menu.header-controls-lang-portal:not(.show) {
    display: none !important;
}
/* Noise texture for dropdown */
.header-controls-lang .dropdown-menu::before,
.dropdown-menu.header-controls-lang-portal::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseLangDropdown'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseLangDropdown)'/%3E%3C/svg%3E");
    opacity: 0.06;
    pointer-events: none;
    z-index: 0;
    border-radius: inherit;
}

/* Rim light for dropdown */
.header-controls-lang .dropdown-menu::after,
.dropdown-menu.header-controls-lang-portal::after {
    content: '';
    position: absolute;
    top: 1px;
    left: 8px;
    right: 8px;
    height: 1px;
    background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
    pointer-events: none;
    z-index: 1;
}

/* Dropdown items */
span.dropdown.header-controls-lang .dropdown-item,
.header-controls-lang .dropdown-item,
body > .dropdown-menu.header-controls-lang-portal .dropdown-item {
    font-family: var(--re-font-ui);
    font-size: 8px;
    color: var(--header-controls-fg) !important;
    padding: 10px 16px;
    transition: all 0.2s ease;
    text-transform: uppercase;
    letter-spacing: 1px;
    position: relative;
    z-index: 2;
}

/* Specificity here has to clear navigation-final-fix.css's
   `.dropdown.header-controls-lang .dropdown-item:hover` so the in-place and
   portaled menus highlight identically. */
span.dropdown.header-controls-lang .dropdown-item:hover,
span.dropdown.header-controls-lang .dropdown-item:focus-visible,
body > .dropdown-menu.header-controls-lang-portal .dropdown-item:hover,
body > .dropdown-menu.header-controls-lang-portal .dropdown-item:focus-visible {
    background: var(--re-surface-active) !important;
    color: #ffffff !important;
}

/* ============================================
   MOBILE RESPONSIVE (max 768px)
   ============================================ */

@media (max-width: 768px) {
    .header-controls-panel {
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        padding: 0;
        backdrop-filter: blur(12px) saturate(160%) contrast(105%);
        -webkit-backdrop-filter: blur(12px) saturate(160%) contrast(105%);
    }
    
    .header-controls-panel::before {
        opacity: 0.03;
    }
    
    .header-controls-theme {
        justify-content: center;
        padding: 12px 20px;
        border-bottom: 1px solid rgba(130, 107, 125, 0.2);
    }
    
    .header-controls-divider {
        display: none;
    }
    
    .header-controls-user {
        justify-content: center;
        padding: 12px 20px;
    }
    
    .header-controls-register {
        font-size: 7px !important;
    }
}

/* ============================================
   VERY SMALL MOBILE (max 480px)
   ============================================ */

@media (max-width: 480px) {
    .header-controls-panel {
        font-size: 7px;
    }
    
    .header-controls-user {
        flex-direction: column;
        gap: 8px;
        text-align: center;
    }
    
    .header-controls-lang {
        width: 100%;
    }
    
    .header-controls-lang-toggle {
        width: 100%;
        justify-content: center;
    }
}

/* ============================================
   TABLET (769px - 992px)
   ============================================ */

@media (min-width: 769px) and (max-width: 992px) {
    .header-controls-panel {
        font-size: 7px;
        gap: 16px;
        padding: 10px 16px;
    }
    
    .header-controls-theme svg {
        font-size: 22px;
    }
    
    .header-controls-profile svg {
        font-size: 18px;
    }
    
    .header-controls-admin-icon svg {
        font-size: 16px;
    }
}

/* ============================================
   LARGE DESKTOP (? 1400px)
   ============================================ */

@media (min-width: 1400px) {
    .header-controls-panel {
        padding: 14px 24px;
        gap: 32px;
    }
    
    .header-controls-user {
        gap: 20px;
    }
}

/* ============================================
   ACCESSIBILITY & FOCUS STATES
   ============================================ */

.header-controls-theme a:focus,
.header-controls-profile:focus,
.header-controls-admin-icon:focus,
.header-controls-lang-toggle:focus {
    outline: 2px solid var(--bs-link-hover-color);
    outline-offset: 2px;
}

/* ============================================
   PRINT STYLES
   ============================================ */

@media print {
    .header-controls-panel {
        display: none;
    }
}

/* ============================================
   LANGUAGE SELECTOR - ICON, ACTIVE STATE, MOTION
   ============================================ */

/* The panel runs at 8px Silkscreen; the globe would be unreadable at that size. */
.header-controls-lang-toggle .bi {
    font-size: 11px;
    line-height: 1;
    color: var(--bs-link-hover-color);
}

/* Which language you are currently on. Without this the menu gives no feedback
   at all about the active choice. */
span.dropdown.header-controls-lang .dropdown-item.active,
body > .dropdown-menu.header-controls-lang-portal .dropdown-item.active {
    background: rgba(130, 107, 125, 0.45) !important;
    color: #ffffff !important;
}

.header-controls-lang .dropdown-item.active::after,
.header-controls-lang-portal .dropdown-item.active::after {
    content: '\2713';
    float: right;
    margin-left: 12px;
    opacity: 0.9;
}

/* Keyboard users need to see where they are; :focus-visible keeps the ring off
   mouse clicks. */
.header-controls-lang .dropdown-item:focus-visible,
.header-controls-lang-portal .dropdown-item:focus-visible {
    outline: 2px solid var(--bs-link-hover-color);
    outline-offset: -2px;
}

/* Short fade so the menu does not snap into place. */
body > .dropdown-menu.header-controls-lang-portal.show {
    animation: header-lang-menu-in 140ms ease-out;
}

@keyframes header-lang-menu-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    body > .dropdown-menu.header-controls-lang-portal.show {
        animation: none;
    }

    .header-controls-theme a:hover,
    .header-controls-lang-toggle:hover {
        transform: none;
    }
}

/* ============================================
   THEME SWITCH STATE

   The control is "Dark Mode [switch] Light Mode". The knob now sits on the
   side of the active mode (it used to sit on the other side, which read as
   inverted), and the active label is brightened so the state is legible
   without having to interpret the knob position at all.
   ============================================ */

.header-controls-theme .theme-label {
    color: var(--re-text-subtle);
    transition: color var(--re-transition);
}

.header-controls-theme .theme-label.is-active {
    color: var(--re-text-color);
    text-shadow: 0 0 8px rgba(var(--re-brand-400-rgb), 0.5);
}

.header-controls-theme a:hover .theme-label {
    color: var(--re-text-muted);
}

.header-controls-theme a:hover .theme-label.is-active {
    color: #ffffff;
}


/* ===== assets/css/user-role-colors.css ===== */
/**
 * User Role Color System
 * Proper CSS for role display colors matching config/roles.php
 * No overrides - clean incremental CSS
 */

/* Base role display styling */
.user-role-display {
    font-weight: bold;
    font-family: var(--re-font-ui);
    font-size: 7px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    opacity: 0.9;
}

/* Administrator - Red */
.user-role-admin {
    color: #ff6b6b;
}

/* Moderator - Teal */
.user-role-moderator {
    color: #4ecdc4;
}

/* Developer - Mint */
.user-role-developer {
    color: #95e1d3;
}

/* Recruiter - Pink */
.user-role-recruiter {
    color: #f38181;
}

/* Editor - Light Green */
.user-role-editor {
    color: #a8e6cf;
}

/* Contributor - Peach */
.user-role-contributor {
    color: #ffd3b6;
}

/* User - Purple (Site Color) */
.user-role-user {
    color: var(--re-brand-400);
}

/* Guest - Gray */
.user-role-guest {
    color: #95a5a6;
}


/* ===== assets/css/z-index-layers.css ===== */
/**
 * Z-Index Layering System - Proper Incremental Structure
 * Single source of truth for all z-index values
 * 
 * Layer Structure:
 * 0:        Particle network (background)
 * 1:        Film grain overlay
 * 5-10:     Main content, cards, containers
 * 15:       Interactive elements (above base content)
 * 20:       Pagination and table controls
 * 100:      Glass containers (about sections, page containers)
 * 1020-1030: Navigation, navbar
 * 1034-1035: Dropdown menus (within their own stacking context)
 * 1040-1070: Modals, tooltips, overlays
 * 9996-9999: Server-info bar, fixed footer, music player
 * 10500:     Menus portaled to <body> to escape a trapping ancestor
 */

/* ================================================
   LAYER 0: BACKGROUND ELEMENTS
   ================================================ */

#particle-network,
#particle-network-container,
canvas#particle-network {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
    pointer-events: none;
    background: transparent;
}

/* ================================================
   LAYER 1: BACKGROUND OVERLAYS
   ================================================ */

.bg {
    z-index: 1;
}

/* ================================================
   LAYER 5: MAIN CONTENT BASE
   ================================================ */

main,
.main-content,
#content,
.container:not(.fixed-bottom *):not(.navbar *) {
    position: relative;
    z-index: 5;
}

/* ================================================
   LAYER 10: CONTENT CARDS & CONTAINERS
   ================================================ */

article,
.article-container,
.article-container-dark,
.article-container-light,
.card,
.card-body,
.breadcrumb,
.quick-link-card {
    position: relative;
    z-index: 10;
}

/* ================================================
   LAYER 15: INTERACTIVE CONTENT

   NOTE ON `position: relative` HERE
   ---------------------------------
   These selectors exist to lift interactive content above the base layer, and
   z-index needs a positioned element to take effect. But `button` combined
   with `:not(...)` has specificity (0,1,1), which quietly outranks any plain
   one-class rule that positions a button deliberately - a component writing
   `.carousel-prev { position: absolute }` (0,1,0) loses and its button drops
   back into normal flow. That is exactly what happened to the About page's
   carousel arrows: they stacked up at the bottom-left corner instead of
   sitting on the sides.

   `:not([class*="-prev"]):not([class*="-next"]):not([class*="-close"])` keeps
   the lift for ordinary buttons while leaving deliberately positioned
   navigation and dismiss controls alone. If you add another positioned button
   pattern, either name it in the same style or give its own rule a two-class
   selector so it outranks this one.
   ================================================ */

.form-control,
.form-select,
.input-group,
button:not(.dropdown-toggle):not([class*="-prev"]):not([class*="-next"]):not([class*="-close"]),
.btn:not(.dropdown-toggle):not([class*="-prev"]):not([class*="-next"]):not([class*="-close"]),
table,
.table,
.list-group,
.list-group-item,
img,
video,
iframe {
    position: relative;
    z-index: 15;
}
/* ================================================
   LAYER 20: PAGINATION & TABLE CONTROLS
   ================================================ */

.pagination,
.pagination-controls,
.table-controls,
.visitors-table-section {
    position: relative;
    z-index: 20;
}

/* ================================================
   LAYER 100: GLASS CONTAINERS
   Keep glass containers at moderate z-index
   ================================================ */

.glass,
.glass-container,
.glass-light,
.glass-dark,
.glass-inset,
.glass-subtle,
.about-section.contained,
.section-container,
.content-panel,
.header-controls-panel {
    position: relative;
    z-index: 100;
}

/* ================================================
   LAYER 1020: FIXED ELEMENTS (Footer/Server Info)
   ================================================ */

.server-info-footer {
    position: fixed;
    z-index: 1020;
}

/* ================================================
   LAYER 1030: NAVIGATION BAR - FIXED AT TOP
   ================================================ */

nav.navbar,
.navbar,
.navbar.fixed-top {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    z-index: 1030 !important;
}

/* ================================================
   LAYER 1040: ALERTS & TOAST MESSAGES
   ================================================ */

.alert,
.toast,
.site-message-container {
    position: relative;
    z-index: 1040;
}

#toast-notification-container {
    position: fixed;
    z-index: 1040;
}

/* ================================================
   LAYER 1045: SIDEBARS & OFF-CANVAS
   ================================================ */

aside,
.sidebar,
.offcanvas {
    position: relative;
    z-index: 1045;
}

/* ================================================
   LAYER 1050: MODALS & DIALOGS
   ================================================ */

.modal,
.modal-dialog,
.modal-content {
    position: relative;
    z-index: 1050;
}

/* ================================================
   LAYER 1060: GALLERIES & LIGHTBOXES
   ================================================ */

.lightbox,
.gallery-item,
.modal-backdrop {
    position: relative;
    z-index: 1060;
}

/* ================================================
   LAYER 1070: TOOLTIPS & POPOVERS
   ================================================ */

.tooltip,
.popover {
    position: relative;
    z-index: 1070;
}

/* ================================================
   LAYER 1080: LOADING SPINNERS
   ================================================ */

.spinner-border,
.spinner-grow {
    position: relative;
    z-index: 1080;
}

/* ================================================
   LAYER 9999: FIXED MUSIC PLAYER (Must be above footer)
   ================================================ */

#persistent-music-player,
.persistent-player {
    position: fixed;
    z-index: 9999 !important;
}

/* ================================================
   LAYER 9997-10000: DEBUG PANELS
   ================================================ */

#debug-panel,
#unified-debug-panel,
.debug-manager-panel {
    position: fixed;
    z-index: 9998;
}

.debug-manager-backdrop {
    position: fixed;
    z-index: 9997;
}

/* ================================================
   LAYER 1035: DROPDOWN MENUS

   A z-index only competes with other z-indexes in the SAME stacking context.
   The eight-nines values that used to live here were not doing anything: a menu
   nested inside .header-controls-panel (backdrop-filter + z-index: 100) or
   inside any .glass container is resolved against that ancestor, so it can never
   climb above the fixed footer or the music player from the inside.

   The reliable fix is to move the menu out of the trapping ancestor - see
   assets/js/language-dropdown-fix.js and the portal layer below. These values
   are what a dropdown needs among its own siblings, nothing more.
   ================================================ */

.dropdown-menu,
.navbar .dropdown-menu {
    position: absolute;
    z-index: 1035;
}

.dropdown-menu.show {
    z-index: 1035;
}

.dropdown,
.navbar .dropdown {
    position: relative;
}

.dropdown.show {
    z-index: 1034;
}

/* ================================================
   LAYER 10500: PORTALED MENUS

   Menus that JavaScript has re-parented to <body> to escape a trapping ancestor.
   Out here they are siblings of the fixed footer (9997), the server-info bar
   (9996) and the music player (9999), so this number is the one that decides
   what paints on top. Positioning is supplied inline by the portal script.
   ================================================ */

body > .dropdown-menu.header-controls-lang-portal {
    z-index: 10500;
}

/* ================================================
   RESPONSIVE ADJUSTMENTS
   ================================================ */

@media (max-width: 992px) {
    /* On mobile, modals should be highest except dropdowns */
    .modal,
    .modal-dialog {
        z-index: 1090;
    }
    
    .modal-backdrop {
        z-index: 1089;
    }
    
    /* Same layering on mobile - see the LAYER 1035 note above for why a bigger
       number here would not help a trapped menu. */
    .dropdown-menu,
    .dropdown-menu.show {
        z-index: 1035;
    }

    body > .dropdown-menu.header-controls-lang-portal {
        z-index: 10500;
    }
}


/* ===== assets/css/navbar-brand-fix.css ===== */
/**
 * Navbar Brand Text Fix
 * Forces Albert Sans font for site name in navigation
 */

/* Force Albert Sans on navbar brand */
.navbar-brand,
.navbar-brand *,
.navbar-brand span {
    font-family: 'Albert Sans', sans-serif !important;
    -webkit-font-smoothing: antialiased !important;
    -moz-osx-font-smoothing: grayscale !important;
}

/* Ensure proper sizing */
.navbar-brand {
    font-size: 1.25rem !important;
    font-weight: 600 !important;
}

@media (max-width: 768px) {
    .navbar-brand {
        font-size: 1rem !important;
    }
}

/* ============================================
   BRAND WORDMARK

   The two halves used to be coloured inline with var(--bs-body-bg) and
   var(--bs-link-hover-color). The first was the page-background variable
   (invisible in dark mode) and the second resolves to the deep plum
   --re-brand-600, which is nearly the same value as the navbar behind it.
   ============================================ */

.navbar-brand-alias {
    color: var(--re-text-color);
}

.navbar-brand-name {
    color: var(--re-brand-200);
}

.navbar-brand:hover .navbar-brand-name {
    color: var(--re-brand-100);
}


/* ===== assets/css/alternating-articles.css ===== */
/**
 * Alternating Article System Styles - GLASS EFFECT
 * Modern glassmorphism design with gradient containers
 * Supports both dark and light themes
 */

/* Article Container Wrapper */
.article-feed {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding: 0;
}

/* Individual Article Card - GLASS EFFECT */
.article-card {
    display: flex;
    flex-direction: row;
    align-items: stretch;
    margin-bottom: 48px;
    border-radius: var(--re-radius-lg);
    overflow: hidden;
    transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    position: relative;
}

/* Dark Theme Styles */
[data-bs-theme="dark"] .article-card.contained {
    background: var(--re-surface);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    box-shadow: 
        0 8px 32px rgba(0, 0, 0, 0.4),
        inset 0 1px 0 rgba(255, 255, 255, 0.05);
    border: 1px solid var(--re-border);
    padding: 32px;
}

[data-bs-theme="dark"] .article-card.open {
    background: var(--re-surface-raised);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    box-shadow: 
        0 8px 32px rgba(130, 107, 125, 0.3),
        inset 0 1px 0 rgba(255, 255, 255, 0.1);
    border: 1px solid var(--re-border);
    padding: 32px;
}

/* Light Theme Styles */
[data-bs-theme="light"] .article-card.contained {
    background: rgba(255, 255, 255, 0.7);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    box-shadow: 
        0 8px 32px rgba(0, 0, 0, 0.1),
        inset 0 1px 0 rgba(255, 255, 255, 0.8);
    border: 1px solid var(--re-border);
    padding: 32px;
}

[data-bs-theme="light"] .article-card.open {
    background: rgba(130, 107, 125, 0.08);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    box-shadow: 
        0 8px 32px rgba(130, 107, 125, 0.15),
        inset 0 1px 0 rgba(255, 255, 255, 0.9);
    border: 1px solid var(--re-border);
    padding: 32px;
}

/*
 * REMOVED: decorative 4px vertical accent strip.
 *
 * This drew a thin light bar down one edge of the panel - left on .contained,
 * right on .open - so the strip appeared to alternate sides between panels.
 * It also occupied the ::before slot that glass-standard.css uses for the
 * panel noise texture, which the bar was suppressing.
 */

/* Date Badge - Glass Effect */
.article-date-badge {
    flex-shrink: 0;
    width: 140px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    position: relative;
    padding: 20px;
    border-radius: var(--re-radius-md);
    margin-right: 24px;
}

/* Dark Theme Date Badge */
[data-bs-theme="dark"] .article-date-badge {
    background: var(--re-surface-raised);
    backdrop-filter: var(--re-glass-subtle);
    -webkit-backdrop-filter: var(--re-glass-subtle);
    border: 1px solid var(--re-border);
}

[data-bs-theme="dark"] .article-card.open .article-date-badge {
    background: var(--re-surface);
    border-color: rgba(130, 107, 125, 0.15);
}

/* Light Theme Date Badge */
[data-bs-theme="light"] .article-date-badge {
    background: rgba(130, 107, 125, 0.12);
    backdrop-filter: var(--re-glass-subtle);
    -webkit-backdrop-filter: var(--re-glass-subtle);
    border: 1px solid var(--re-border);
}

[data-bs-theme="light"] .article-card.open .article-date-badge {
    background: rgba(255, 255, 255, 0.5);
    border-color: var(--re-border);
}

/* Date Badge - Day Number */
.article-date-badge .day {
    font-family: var(--re-font-body);
    font-size: 72px;
    font-weight: 900;
    line-height: 0.9;
    text-shadow: -6px 0 12px var(--bs-link-color);
    margin: 0;
    padding: 0;
}

[data-bs-theme="dark"] .article-date-badge .day {
    color: rgba(255, 255, 255, 0.95);
}

[data-bs-theme="light"] .article-date-badge .day {
    color: rgba(93, 75, 96, 0.9);
}

/* Date Badge - Month */
.article-date-badge .month {
    font-family: var(--re-font-body);
    font-size: 16px;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--bs-link-color);
    text-shadow: -4px 0 8px var(--bs-link-color);
    letter-spacing: 2px;
    margin-top: -4px;
}

/* Date Badge - Year */
.article-date-badge .year {
    font-family: var(--re-font-body);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 1px;
    margin-top: 4px;
    opacity: 0.85;
}

[data-bs-theme="dark"] .article-date-badge .year {
    color: rgba(255, 255, 255, 0.75);
}

[data-bs-theme="light"] .article-date-badge .year {
    color: rgba(93, 75, 96, 0.7);
}

/* Article Content */
.article-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 0;
}

/* Article Title */
.article-title {
    color: #fff;
    font-family: var(--re-font-body);
    font-size: 28px;
    font-weight: 700;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
    margin: 0 0 12px 0;
    line-height: 1.3;
    transition: color 0.3s ease;
}

[data-bs-theme="dark"] .article-title {
    color: rgba(255, 255, 255, 0.95);
}

[data-bs-theme="light"] .article-title {
    color: var(--re-brand-400);
}

.article-title a {
    color: inherit;
    text-decoration: none;
    transition: color 0.3s ease;
}

.article-title a:hover {
    color: var(--bs-link-hover-color);
    text-shadow: 0 2px 8px rgba(130, 107, 125, 0.5);
}

/* Gradient HR Divider */
.article-divider {
    height: 1px;
    margin: 16px 0;
    border: none;
    background: linear-gradient(90deg, 
        transparent 0%, 
        rgba(130, 107, 125, 0.3) 10%, 
        rgba(130, 107, 125, 0.6) 50%, 
        rgba(130, 107, 125, 0.3) 90%, 
        transparent 100%);
    box-shadow: 0 1px 2px rgba(130, 107, 125, 0.2);
}

[data-bs-theme="light"] .article-divider {
    background: linear-gradient(90deg, 
        transparent 0%, 
        rgba(130, 107, 125, 0.2) 10%, 
        rgba(130, 107, 125, 0.5) 50%, 
        rgba(130, 107, 125, 0.2) 90%, 
        transparent 100%);
}

/* Article Excerpt */
.article-excerpt {
    color: var(--re-brand-400);
    font-family: var(--re-font-body);
    font-size: 15px;
    line-height: 1.6;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
    margin: 0 0 16px 0;
}

[data-bs-theme="dark"] .article-excerpt {
    color: rgba(255, 255, 255, 0.85);
}

[data-bs-theme="light"] .article-excerpt {
    color: rgba(0, 0, 0, 0.75);
}

/* Article Meta */
.article-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: auto;
    padding-top: 16px;
    border-top: 1px solid var(--re-border);
}

.article-meta-item {
    display: flex;
    align-items: center;
    gap: 6px;
    font-family: var(--re-font-ui);
    font-size: 8px;
    text-transform: uppercase;
    letter-spacing: 1px;
    padding: 4px 10px;
    backdrop-filter: var(--re-glass-subtle);
    -webkit-backdrop-filter: var(--re-glass-subtle);
    border-radius: var(--re-radius-sm);
    border: 1px solid var(--re-border);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}

[data-bs-theme="dark"] .article-meta-item {
    background: var(--re-surface);
}

[data-bs-theme="light"] .article-meta-item {
    background: rgba(255, 255, 255, 0.4);
}

.article-meta-label {
    color: var(--bs-link-hover-color);
    text-shadow: 0 1px rgba(0, 0, 0, 0.3);
    font-weight: 600;
}

.article-meta-value {
    text-shadow: 0 1px rgba(0, 0, 0, 0.3);
}

[data-bs-theme="dark"] .article-meta-value {
    color: rgba(255, 255, 255, 0.9);
}

[data-bs-theme="light"] .article-meta-value {
    color: rgba(0, 0, 0, 0.8);
}

/* Category Badge - Glass Effect */
.article-category {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    backdrop-filter: var(--re-glass-subtle);
    -webkit-backdrop-filter: var(--re-glass-subtle);
    border-radius: var(--re-radius-sm);
    border: 1px solid var(--re-border);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
    font-family: var(--re-font-ui);
    font-size: 8px;
    text-transform: uppercase;
    letter-spacing: 1px;
    margin-bottom: 12px;
}

[data-bs-theme="dark"] .article-category {
    background: var(--re-surface-raised);
    color: rgba(255, 255, 255, 0.9);
}

[data-bs-theme="light"] .article-category {
    background: var(--re-surface-raised);
    color: rgba(0, 0, 0, 0.85);
}

/* Read More Button - Glass Effect */
.article-read-more {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 12px 24px;
    backdrop-filter: var(--re-glass-subtle);
    -webkit-backdrop-filter: var(--re-glass-subtle);
    border: 1px solid var(--re-border);
    box-shadow: 
        0 4px 12px rgba(0, 0, 0, 0.3),
        inset 0 1px 0 rgba(255, 255, 255, 0.05);
    font-family: var(--re-font-ui);
    font-size: 8px;
    text-transform: uppercase;
    letter-spacing: 1px;
    border-radius: var(--re-radius-md);
    cursor: pointer;
    transition: all 0.3s ease;
    text-decoration: none;
    align-self: flex-start;
    margin-top: 16px;
    line-height: 1;
    min-height: 40px;
}

[data-bs-theme="dark"] .article-read-more {
    background: var(--re-surface);
    color: rgba(255, 255, 255, 0.95);
}

[data-bs-theme="light"] .article-read-more {
    background: rgba(255, 255, 255, 0.5);
    color: rgba(93, 75, 96, 0.95);
}

.article-read-more:hover {
    background: var(--re-surface-hover);
    border-color: var(--re-border-strong);
    transform: translateX(4px);
    box-shadow: 
        0 6px 16px rgba(130, 107, 125, 0.4),
        inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

[data-bs-theme="dark"] .article-read-more:hover {
    color: rgba(255, 255, 255, 0.95);
}

[data-bs-theme="light"] .article-read-more:hover {
    color: rgba(0, 0, 0, 0.95);
}

/* Featured Article Badge */
.article-featured-badge {
    position: absolute;
    top: 16px;
    right: 16px;
    padding: 8px 16px;
    background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
    color: #000;
    font-family: var(--re-font-ui);
    font-size: 8px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1px;
    border-radius: var(--re-radius-sm);
    box-shadow: 0 4px 8px rgba(255, 215, 0, 0.4);
    animation: pulse 2s ease-in-out infinite;
}

/* Edit Article Button - Glass Effect */
.article-edit-button {
    z-index: 10;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 12px 24px;
    backdrop-filter: var(--re-glass-subtle);
    -webkit-backdrop-filter: var(--re-glass-subtle);
    border: 1px solid var(--re-border);
    box-shadow: 
        0 4px 12px rgba(0, 0, 0, 0.3),
        inset 0 1px 0 rgba(255, 255, 255, 0.05);
    font-family: var(--re-font-ui);
    font-size: 8px;
    text-transform: uppercase;
    letter-spacing: 1px;
    border-radius: var(--re-radius-md);
    cursor: pointer;
    transition: all 0.3s ease;
    text-decoration: none;
    line-height: 1;
    min-height: 40px;
    margin: 0;
}

[data-bs-theme="dark"] .article-edit-button {
    background: var(--re-surface);
    color: rgba(255, 255, 255, 0.95);
}

[data-bs-theme="light"] .article-edit-button {
    background: rgba(255, 255, 255, 0.5);
    color: rgba(93, 75, 96, 0.95);
}

.article-edit-button:hover {
    background: rgba(255, 193, 7, 0.3);
    border-color: rgba(255, 193, 7, 0.5);
    transform: translateX(4px);
    box-shadow: 
        0 6px 16px rgba(255, 193, 7, 0.4),
        inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

[data-bs-theme="dark"] .article-edit-button:hover {
    color: rgba(255, 255, 255, 0.95);
}

[data-bs-theme="light"] .article-edit-button:hover {
    color: rgba(0, 0, 0, 0.95);
}

.article-edit-button i {
    transition: transform 0.3s ease;
    font-size: 12px;
}

.article-edit-button:hover i {
    transform: scale(1.1);
}

/* Action Buttons Container */
.article-card .article-read-more,
.article-card .article-edit-button {
    flex-shrink: 0;
    margin-top: 0;
}

@keyframes pulse {
    0%, 100% {
        transform: scale(1);
    }
    50% {
        transform: scale(1.05);
    }
}

/* View Count Badge - Glass Effect */
.article-views {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border-radius: var(--re-radius-sm);
    border: 1px solid rgba(130, 107, 125, 0.2);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
    font-family: var(--re-font-ui);
    font-size: 8px;
}

[data-bs-theme="dark"] .article-views {
    background: rgba(0, 0, 0, 0.2);
    color: rgba(255, 255, 255, 0.9);
}

[data-bs-theme="light"] .article-views {
    background: rgba(255, 255, 255, 0.4);
    color: rgba(0, 0, 0, 0.85);
}

/* Author Avatar */
.article-author-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 2px solid var(--bs-link-hover-color);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    object-fit: cover;
}

/* Tags - Glass Effect */
.article-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 12px;
}

.article-tag {
    padding: 4px 10px;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid rgba(130, 107, 125, 0.3);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
    border-radius: var(--re-radius-sm);
    font-family: var(--re-font-ui);
    font-size: 8px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    transition: all 0.3s ease;
}

[data-bs-theme="dark"] .article-tag {
    background: rgba(130, 107, 125, 0.2);
    color: rgba(255, 255, 255, 0.9);
}

[data-bs-theme="light"] .article-tag {
    background: rgba(130, 107, 125, 0.15);
    color: rgba(0, 0, 0, 0.85);
}

.article-tag:hover {
    background: rgba(130, 107, 125, 0.4);
    border-color: rgba(130, 107, 125, 0.6);
    transform: translateY(-2px);
    box-shadow: 
        0 4px 12px rgba(130, 107, 125, 0.3),
        inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

[data-bs-theme="dark"] .article-tag:hover {
    color: rgba(255, 255, 255, 0.95);
}

[data-bs-theme="light"] .article-tag:hover {
    color: rgba(0, 0, 0, 0.9);
}

/* Loading State */
.article-card.loading {
    opacity: 0.5;
    pointer-events: none;
}

.article-card.loading::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(90deg, transparent 0%, rgba(130, 107, 125, 0.2) 50%, transparent 100%);
    animation: shimmer 1.5s infinite;
}

@keyframes shimmer {
    0% {
        transform: translateX(-100%);
    }
    100% {
        transform: translateX(100%);
    }
}

/* Responsive Design */
@media (max-width: 768px) {
    .article-card {
        flex-direction: column;
        margin-bottom: 32px;
        padding: 24px !important;
    }
    
    .article-card .article-read-more,
    .article-card .article-edit-button {
        font-size: 7px;
        padding: 10px 20px;
    }
    
    .article-date-badge {
        width: 100%;
        flex-direction: row;
        justify-content: space-between;
        padding: 16px;
        border-bottom: 1px solid rgba(130, 107, 125, 0.3);
        border-radius: var(--re-radius-md) var(--re-radius-md) 0 0;
        margin-right: 0;
        margin-bottom: 16px;
    }
    
    .article-date-badge .day {
        font-size: 48px;
        color: #fff;
    }
    
    .article-date-badge .month {
        font-size: 8px;
        margin-top: 0;
    }
    
    .article-date-badge .year {
        position: static;
        writing-mode: horizontal-tb;
        font-size: 8px;
        margin-top: 0;
    }
    
    .article-content {
        padding: 0;
    }
    
    .article-title {
        font-size: 22px;
    }
    
    .article-excerpt {
        font-size: 14px;
    }
}

/* Animation on scroll */
.article-card[data-aos] {
    transition-property: transform, opacity;
}

/* Print Styles */
@media print {
    .article-card {
        break-inside: avoid;
        page-break-inside: avoid;
    }
    
    .article-read-more,
    .article-edit-button,
    .article-featured-badge {
        display: none;
    }
}


/* ===== assets/css/Articles-Cards-images.css ===== */
.fit-cover {
  object-fit: cover;
}



/* ===== assets/css/page-standard.css ===== */
/**
 * PAGE STANDARD - Unified styling across all pages
 * =================================================
 * Ensures consistent look and feel matching the About page standard.
 * All pages should use these classes for consistency.
 * 
 * @author RealityEngine
 * @version 1.0
 */

/* ============================================
   PAGE HEADER SECTIONS
   ============================================ */

.page-header {
  /* Was a near-transparent gradient with no backdrop-filter, so it read as a
     faint outline rather than a panel while everything around it was glass. */
  background: var(--re-surface-raised);
  backdrop-filter: var(--re-glass);
  -webkit-backdrop-filter: var(--re-glass);
  box-shadow: var(--re-shadow-2);
  padding: var(--re-space-6);
  border-radius: var(--re-radius-lg);
  border: 1px solid var(--re-border);
  position: relative;
  overflow: hidden;
  margin-bottom: var(--re-space-6);
}

/*
 * REMOVED: decorative 4px vertical accent strip.
 *
 * This drew a thin light bar down one edge of the panel - left on .contained,
 * right on .open - so the strip appeared to alternate sides between panels.
 * It also occupied the ::before slot that glass-standard.css uses for the
 * panel noise texture, which the bar was suppressing.
 */

.page-header-inner {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
}

.page-header-icon {
  width: 60px;
  height: 60px;
  background: rgba(130,107,125,0.3);
  border-radius: var(--re-radius-lg);
  display: flex;
  align-items: center;
  justify-content: center;
}

.page-header-icon i {
  font-size: 32px;
  color: var(--re-brand-400);
}

.page-header-content {
  flex: 1;
}

.page-title {
  color: var(--re-heading-color);
  font-family: var(--re-font-display);
  font-size: var(--re-size-3xl);
  font-weight: 700;
  margin: 0;
  line-height: 1.1;
}

.page-subtitle {
  color: var(--re-text-color);
  opacity: 0.7;
  font-family: var(--re-font-ui);
  font-size: 8px;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 1px;
}

.page-description {
  color: var(--re-text-color);
  opacity: 0.8;
  font-size: 16px;
  margin: 0;
  line-height: 1.6;
  font-family: var(--re-font-body);
}

/* ============================================
   CONTENT PANELS - Glass morphism standard
   ============================================ */

.content-panel {
  background: var(--re-surface);
  backdrop-filter: var(--re-glass);
  -webkit-backdrop-filter: var(--re-glass);
  border: 1px solid var(--re-border);
  border-radius: var(--re-radius-lg);
  padding: 24px;
  margin-bottom: 32px;
  position: relative;
  overflow: hidden;
}

.content-panel::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noisePanel'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noisePanel)'/%3E%3C/svg%3E");
  opacity: 0.06;
  pointer-events: none;
  z-index: 0;
  border-radius: inherit;
}

.content-panel::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
  pointer-events: none;
  z-index: 1;
}

.content-panel > * {
  position: relative;
  z-index: 2;
}

/* ============================================
   FILTER BAR STANDARD
   ============================================ */

.filter-bar {
  background: var(--re-surface);
  backdrop-filter: var(--re-glass);
  -webkit-backdrop-filter: var(--re-glass);
  box-shadow: var(--re-shadow-1);
  padding: var(--re-space-5);
  border-radius: var(--re-radius-lg);
  border: 1px solid var(--re-border);
  margin-bottom: var(--re-space-6);
}

.filter-bar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
}

.filter-section {
  flex: 1;
  min-width: 300px;
}

.filter-section-title {
  color: var(--re-brand-400);
  font-family: var(--re-font-ui);
  font-size: 8px;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin: 0 0 12px 0;
}

.filter-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.filter-btn {
  padding: var(--re-space-2) var(--re-space-4);
  background: var(--re-surface-sunken);
  border: 1px solid var(--re-border);
  border-radius: var(--re-radius-md);
  color: var(--re-text-color);
  text-decoration: none;
  font-size: var(--re-size-sm);
  font-family: var(--re-font-body);
  font-weight: 600;
  transition: all var(--re-transition);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.filter-btn:hover {
  background: var(--re-surface-hover);
  border-color: var(--re-border-strong);
  color: var(--re-heading-color);
}

/* The selected filter has to be unmistakable against its neighbours - hover
   and active used to resolve to the same tint. */
.filter-btn.active {
  background: var(--re-brand-600);
  border-color: var(--re-brand-400);
  color: #ffffff;
}

.filter-btn-count {
  font-family: var(--re-font-ui);
  font-size: 8px;
  opacity: 0.7;
}

/* ============================================
   SEARCH BAR STANDARD
   ============================================ */

.search-row {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--re-border);
}

.search-form {
  display: flex;
  gap: 12px;
  align-items: center;
}

.search-input-wrapper {
  flex: 1;
  position: relative;
}

.search-input-wrapper i {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--re-text-color);
  opacity: 0.5;
  font-size: 16px;
}

.search-input {
  width: 100%;
  padding: 12px 12px 12px 40px;
  background: var(--re-surface-sunken);
  border: 1px solid var(--re-border);
  border-radius: var(--re-radius-md);
  color: var(--re-text-color);
  font-family: var(--re-font-body);
  font-size: 14px;
  transition: border-color 0.3s ease;
}

.search-input:focus {
  border-color: var(--re-border-strong);
  outline: none;
}

.search-input::placeholder {
  color: rgba(238, 224, 218, 0.5);
}

/* ============================================
   BUTTONS STANDARD
   ============================================ */

.btn-primary-standard {
  padding: 12px 24px;
  background: var(--re-brand-600);
  color: white;
  border: none;
  border-radius: var(--re-radius-md);
  font-family: var(--re-font-body);
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: all 0.3s ease;
  text-decoration: none;
}

.btn-primary-standard:hover {
  background: var(--re-brand-500);
  transform: translateY(-2px);
  color: white;
}

/* This was red - the same colour as a delete action - even though it is used
   for neutral things like "clear filters". Red is now reserved for
   .btn-danger-standard below. */
.btn-secondary-standard {
  padding: 12px var(--re-space-5);
  background: var(--re-surface-sunken);
  color: var(--re-text-color);
  border: 1px solid var(--re-border);
  border-radius: var(--re-radius-md);
  text-decoration: none;
  font-family: var(--re-font-body);
  font-weight: 600;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: var(--re-space-2);
  transition: all var(--re-transition);
}

.btn-secondary-standard:hover {
  background: var(--re-surface-hover);
  border-color: var(--re-border-strong);
  color: var(--re-heading-color);
  transform: var(--re-lift);
}

/* Destructive actions only. */
.btn-danger-standard {
  padding: 12px var(--re-space-5);
  background: rgba(220, 53, 69, 0.22);
  color: #f1919b;
  border: 1px solid rgba(220, 53, 69, 0.45);
  border-radius: var(--re-radius-md);
  text-decoration: none;
  font-family: var(--re-font-body);
  font-weight: 600;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: var(--re-space-2);
  transition: all var(--re-transition);
}

.btn-danger-standard:hover {
  background: rgba(220, 53, 69, 0.34);
  color: #ffd7db;
}

.btn-icon {
  padding: 10px 14px;
  background: var(--re-surface-raised);
  backdrop-filter: var(--re-glass-subtle);
  -webkit-backdrop-filter: var(--re-glass-subtle);
  border: 1px solid var(--re-border-strong);
  color: var(--re-text-color);
  border-radius: var(--re-radius-sm);
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  text-decoration: none;
}

.btn-icon:hover {
  background: var(--re-surface-active);
  transform: translateY(-2px);
  color: var(--re-text-color);
}

/* ============================================
   CARD GRID STANDARD
   ============================================ */

.cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 24px;
  margin-bottom: 48px;
}

.standard-card {
  background: var(--re-surface);
  backdrop-filter: var(--re-glass);
  -webkit-backdrop-filter: var(--re-glass);
  border: 1px solid var(--re-border);
  border-radius: var(--re-radius-lg);
  overflow: hidden;
  transition: all 0.3s ease;
  position: relative;
  display: flex;
  flex-direction: column;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5),
              inset 0 1px 0 0 rgba(255, 255, 255, 0.15),
              inset 0 -1px 0 0 rgba(0, 0, 0, 0.15);
}

.standard-card:hover {
  background: var(--re-surface-hover);
  border-color: var(--re-border-strong);
  transform: translateY(-8px);
  box-shadow: 0 12px 40px rgba(130, 107, 125, 0.6),
              inset 0 1px 0 0 rgba(255, 255, 255, 0.25),
              inset 0 -1px 0 0 rgba(0, 0, 0, 0.15);
}

.card-media {
  position: relative;
  overflow: hidden;
  height: 220px;
  background: linear-gradient(135deg, rgba(93, 75, 96, 0.2) 0%, rgba(0, 0, 0, 0.3) 100%);
  border-radius: var(--re-radius-lg) var(--re-radius-lg) 0 0;
}

.card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.5s ease, filter 0.3s ease;
}

.standard-card:hover .card-media img {
  transform: scale(1.08);
  filter: brightness(1.1);
}

.card-media-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.7) 100%);
  pointer-events: none;
}

.card-badge {
  position: absolute;
  padding: 6px 12px;
  background: rgba(0, 0, 0, 0.85);
  backdrop-filter: var(--re-glass);
  -webkit-backdrop-filter: var(--re-glass);
  border-radius: var(--re-radius-sm);
  border: 1px solid var(--re-border);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6),
              inset 0 1px 0 0 rgba(255, 255, 255, 0.1);
  z-index: 3;
}

.card-badge-top-right {
  top: 12px;
  right: 12px;
}

.card-badge-top-left {
  top: 12px;
  left: 12px;
}

.card-badge i {
  font-size: 14px;
}

.card-badge span {
  color: var(--re-text-color);
  font-family: var(--re-font-ui);
  font-size: 8px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-left: 4px;
}

.card-content {
  padding: 20px;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.card-title {
  color: var(--re-text-color);
  font-family: var(--re-font-body);
  font-size: 20px;
  font-weight: 700;
  margin: 0 0 8px 0;
}

.card-description {
  color: var(--re-text-color);
  opacity: 0.8;
  font-family: var(--re-font-body);
  font-size: 14px;
  line-height: 1.6;
  margin: 0 0 16px 0;
  flex: 1;
}

.card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 16px;
}

.card-tag {
  padding: 4px 10px;
  background: var(--re-surface-raised);
  backdrop-filter: var(--re-glass-subtle);
  -webkit-backdrop-filter: var(--re-glass-subtle);
  border-radius: var(--re-radius-sm);
  font-family: var(--re-font-ui);
  font-size: 8px;
  color: var(--re-text-color);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border: 1px solid var(--re-border);
}

.card-actions {
  display: flex;
  gap: 8px;
}

.card-btn-primary {
  flex: 1;
  padding: 10px;
  background: linear-gradient(135deg, var(--re-brand-400) 0%, var(--re-brand-500) 100%);
  color: white;
  text-align: center;
  text-decoration: none;
  border-radius: var(--re-radius-sm);
  font-family: var(--re-font-body);
  font-weight: 600;
  font-size: 13px;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px solid var(--re-border);
}

.card-btn-primary:hover {
  background: linear-gradient(135deg, var(--re-brand-300) 0%, var(--re-brand-400) 100%);
  transform: translateY(-2px);
  color: white;
}

/* ============================================
   SECTION HEADER WITH ACCENT BAR
   ============================================ */

.section-header-accent {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 24px;
}

/*
 * REMOVED: .accent-bar
 *
 * A 4px x 32px vertical strip that sat beside section headings - the same
 * decorative device as the panel-edge bars, anchored to a heading rather than
 * a panel edge. The seven elements using it have been taken out of the
 * templates; .section-header-accent below is now just the heading row.
 */

.section-header-text .section-title {
  color: var(--re-text-color);
  font-family: var(--re-font-body);
  font-size: 24px;
  font-weight: 700;
  margin: 0;
  line-height: 1.2;
  display: flex;
  align-items: center;
}

.section-header-text .section-title i {
  color: var(--re-brand-400);
  margin-right: 12px;
}

.section-header-text .section-meta {
  color: var(--re-text-color);
  opacity: 0.7;
  font-family: var(--re-font-ui);
  font-size: 8px;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 1px;
}

/* ============================================
   PAGINATION STANDARD
   ============================================ */

.pagination-standard {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-bottom: 48px;
}

.pagination-btn {
  padding: 10px 16px;
  background: rgba(130,107,125,0.3);
  border: 1px solid var(--re-border-strong);
  color: var(--re-text-color);
  border-radius: var(--re-radius-sm);
  text-decoration: none;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  gap: 6px;
}

.pagination-btn:hover {
  background: var(--re-surface-active);
  color: var(--re-text-color);
}

.pagination-page {
  padding: 10px 14px;
  background: var(--re-surface);
  border: 1px solid var(--re-border);
  color: var(--re-text-color);
  border-radius: var(--re-radius-sm);
  text-decoration: none;
  font-family: var(--re-font-body);
  font-weight: 600;
  transition: all 0.3s ease;
}

.pagination-page:hover {
  background: var(--re-surface-hover);
  color: var(--re-text-color);
}

.pagination-page.active {
  background: var(--re-brand-600);
  border-color: var(--re-brand-600);
}

/* ============================================
   EMPTY STATE STANDARD
   ============================================ */

.empty-state {
  text-align: center;
  padding: var(--re-space-7) var(--re-space-5);
  background: var(--re-surface);
  backdrop-filter: var(--re-glass);
  -webkit-backdrop-filter: var(--re-glass);
  box-shadow: var(--re-shadow-1);
  border-radius: var(--re-radius-lg);
  border: 1px solid var(--re-border);
}

.empty-state-icon {
  font-size: 56px;
  color: var(--re-brand-400);
  opacity: 0.6;
  display: block;
  margin-bottom: var(--re-space-4);
}

.empty-state-title {
  color: var(--re-heading-color);
  font-family: var(--re-font-display);
  font-size: var(--re-size-xl);
  margin-bottom: var(--re-space-2);
}

.empty-state-text {
  color: var(--re-text-color);
  opacity: 0.7;
  font-family: var(--re-font-body);
  font-size: 16px;
  margin-bottom: 24px;
}

/* ============================================
   ARTICLE CONTAINERS (Home page)
   ============================================ */

/* The alternating home-page stripe. These are one component with two tints,
   not two designs: everything except the background is shared, so the rhythm
   reads as alternation rather than as a styling accident. */
.article-container-dark,
.article-container-light {
  padding: var(--re-space-6);
  border-radius: var(--re-radius-lg);
  margin-bottom: var(--re-space-6);
  position: relative;
  overflow: hidden;
  border: 1px solid var(--re-border);
  backdrop-filter: var(--re-glass);
  -webkit-backdrop-filter: var(--re-glass);
  box-shadow: var(--re-shadow-1);
  transition: border-color var(--re-transition), box-shadow var(--re-transition);
}

.article-container-dark:hover,
.article-container-light:hover {
  border-color: var(--re-border-strong);
  box-shadow: var(--re-shadow-2);
}

.article-container-dark  { background: var(--re-surface); }
.article-container-light { background: var(--re-surface-raised); }

.article-title {
  color: var(--re-brand-400);
  font-family: var(--re-font-body);
  font-size: 28px;
  font-weight: 700;
  line-height: 1.3;
  margin: 0;
}

.article-title a {
  color: inherit;
  text-decoration: none;
  transition: color 0.3s ease;
}

.article-title a:hover {
  color: var(--bs-link-hover-color);
}

.article-meta-item {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--re-text-color);
  opacity: 0.8;
}

.article-separator {
  border: none;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(130, 107, 125, 0.5) 50%, transparent 100%);
  margin: 60px auto;
  max-width: 90%;
}

/* ============================================
   QUICK LINKS CARDS
   ============================================ */

.quick-link-card {
  display: flex;
  align-items: center;
  gap: var(--re-space-3);
  padding: var(--re-space-5);
  background: var(--re-surface-raised);
  /* Was the only card on the site with no backdrop-filter, so it sat flat
     among glass siblings. */
  backdrop-filter: var(--re-glass);
  -webkit-backdrop-filter: var(--re-glass);
  box-shadow: var(--re-shadow-1);
  border: 1px solid var(--re-border);
  border-radius: var(--re-radius-lg);
  text-decoration: none;
  transition: all var(--re-transition);
}

.quick-link-card:hover {
  background: var(--re-surface-hover);
  border-color: var(--re-border-strong);
  transform: translateY(-4px);
  box-shadow: 0 8px 16px rgba(130,107,125,0.3);
}

.quick-link-icon {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(130,107,125,0.3);
  border-radius: var(--re-radius-md);
}

.quick-link-icon i {
  font-size: 24px;
  color: var(--re-brand-400);
}

.quick-link-text {
  flex: 1;
}

.quick-link-title {
  color: var(--re-text-color);
  font-family: var(--re-font-body);
  font-size: 16px;
  font-weight: 600;
}

.quick-link-subtitle {
  color: var(--re-text-color);
  opacity: 0.6;
  font-family: var(--re-font-ui);
  font-size: 8px;
  text-transform: uppercase;
  margin-top: 2px;
}

.quick-link-card i.bi-arrow-right {
  font-size: 18px;
  color: var(--bs-link-hover-color);
  flex-shrink: 0;
  transition: transform 0.3s ease;
}

.quick-link-card:hover i.bi-arrow-right {
  transform: translateX(4px);
}

/* ============================================
   SELECT DROPDOWNS STANDARD
   ============================================ */

.select-standard {
  width: 100%;
  padding: 8px 12px;
  background: var(--re-surface-sunken);
  border: 1px solid var(--re-border);
  border-radius: var(--re-radius-md);
  color: var(--re-text-color);
  font-family: var(--re-font-body);
  font-size: 14px;
  cursor: pointer;
}

.select-standard:focus {
  border-color: var(--re-border-strong);
  outline: none;
}

/* ============================================
   VIEW TOGGLE BUTTONS
   ============================================ */

.view-toggle {
  display: flex;
  gap: 8px;
}

.view-toggle-btn {
  padding: 10px 14px;
  background: var(--re-surface);
  border: 1px solid var(--re-border);
  border-radius: var(--re-radius-sm);
  color: var(--re-text-color);
  cursor: pointer;
  transition: all 0.3s ease;
}

.view-toggle-btn.active,
.view-toggle-btn:hover {
  background: var(--re-surface-active);
  border-color: var(--re-border-strong);
}

.view-toggle-btn i {
  font-size: 18px;
}

/* ============================================
   RESPONSIVE ADJUSTMENTS
   ============================================ */

@media (max-width: 768px) {
  .page-title {
    font-size: 28px;
  }
  
  .page-header {
    padding: 20px;
  }
  
  .page-header-inner {
    flex-direction: column;
    text-align: center;
  }
  
  .cards-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  
  .filter-bar-inner {
    flex-direction: column;
  }
  
  .filter-section {
    min-width: 100%;
  }
  
  .search-form {
    flex-direction: column;
  }
  
  .search-form > * {
    width: 100%;
  }
  
  .pagination-standard {
    flex-wrap: wrap;
  }
  
  .view-toggle {
    display: none;
  }
  
  .article-container-dark,
  .article-container-light {
    padding: 20px;
  }
  
  .article-title {
    font-size: 1.5rem;
  }
}

/* ============================================
   LEGAL PAGES (privacy, terms)
   Long-form prose inside the standard .content-panel glass. The panel paints a
   noise layer at z-index 0 and a rim light at 1, so the text needs to sit above
   both - see .content-panel::before / ::after.
   ============================================ */

.page-legal > * {
  position: relative;
  z-index: 2;
}

.page-legal-title {
  font-family: var(--re-font-body);
  color: var(--bs-link-hover-color);
  text-shadow: 0 1px rgba(0, 0, 0, 0.5);
  margin-bottom: 4px;
}

.page-legal-updated {
  font-family: var(--re-font-ui);
  font-size: 8px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--re-brand-400);
  margin-bottom: 28px;
}

.page-legal h2 {
  font-family: var(--re-font-body);
  font-size: 20px;
  color: #ffffff;
  margin-top: 28px;
  margin-bottom: 10px;
}

.page-legal p {
  color: rgba(255, 255, 255, 0.82);
  line-height: 1.7;
  max-width: 68ch;
}

.page-legal a {
  color: var(--bs-link-hover-color);
}

/* ============================================
   ERROR PAGES

   404 used to carry its own inline glass panel (a different background,
   radius, blur and shadow from every other panel on the site) with a 120px
   Albert Sans numeral. It is now a .content-panel like everything else.
   ============================================ */

.error-panel {
  padding: var(--re-space-7) var(--re-space-5);
  text-align: center;
}

.error-code {
  font-family: var(--re-font-display);
  font-size: clamp(72px, 14vw, 140px);
  line-height: 1;
  margin: 0 0 var(--re-space-3);
  color: var(--re-brand-400);
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.45);
}

.error-title {
  font-family: var(--re-font-display);
  color: var(--re-heading-color);
  margin: 0 0 var(--re-space-3);
}

.error-text {
  color: var(--re-text-muted);
  font-family: var(--re-font-body);
  margin: 0 0 var(--re-space-5);
}

/* .btn-primary-standard is display:flex, which would stretch it full width
   inside a centred column. */
.error-action {
  display: inline-flex !important;
  margin: 0 auto;
}


/* ===== assets/css/about-page.css ===== */
/**
 * About Page - Dark Glass Redesign
 * ================================
 * Integrates the About profile, skills, experience, and gaming carousels
 * into the main site's premium dark glassmorphism aesthetic.
 */

/* =========================================
   1. HERO SECTION
   ========================================= */
.about-hero {
    position: relative;
    padding: 60px 0;
    margin-top: 40px;
}

.about-hero-container {
    background: var(--re-surface);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-lg);
    box-shadow: 0 16px 40px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.05);
    padding: 48px;
    position: relative;
    overflow: hidden;
}

/* Subtle gradient orb behind profile */
.about-hero-container::before {
    content: '';
    position: absolute;
    top: -50%;
    left: -10%;
    width: 60%;
    height: 200%;
    background: radial-gradient(circle, rgba(130,107,125,0.1) 0%, transparent 70%);
    z-index: 0;
    pointer-events: none;
}

.about-hero-container > .row {
    position: relative;
    z-index: 1;
}

/* Profile Image */
.profile-image-wrapper {
    position: relative;
    display: inline-block;
    margin-bottom: 24px;
}

.profile-image {
    width: 200px;
    height: 200px;
    object-fit: cover;
    border-radius: 50%;
    border: 3px solid transparent;
    box-shadow: 0 8px 32px rgba(0,0,0,0.5);
    position: relative;
    z-index: 2;
}

.profile-ring {
    position: absolute;
    top: -8px;
    left: -8px;
    right: -8px;
    bottom: -8px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--bs-primary) 0%, var(--re-brand-400) 100%);
    z-index: 1;
    animation: spin-slow 10s linear infinite;
    opacity: 0.5;
}

@keyframes spin-slow {
    100% { transform: rotate(360deg); }
}

/* Profile Text */
.profile-name {
    font-size: 2.8rem;
    margin-bottom: 8px;
    color: var(--bs-heading-color);
    text-shadow: 0 4px 12px rgba(0,0,0,0.5);
}

.profile-tagline {
    font-family: var(--re-font-ui);
    color: var(--bs-primary);
    font-size: 14px;
    letter-spacing: 2px;
    text-transform: uppercase;
    margin-bottom: 24px;
}

/* Social Links */
.social-links {
    display: flex;
    justify-content: center;
    gap: 16px;
}

.social-link {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--re-radius-lg);
    background: rgba(130,107,125,0.15);
    border: 1px solid rgba(130,107,125,0.3);
    color: var(--re-text-color);
    font-size: 20px;
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    backdrop-filter: blur(8px);
}

.social-link:hover {
    background: var(--bs-primary);
    color: #fff;
    transform: translateY(-4px);
    box-shadow: 0 8px 24px rgba(var(--bs-primary-rgb), 0.4);
    border-color: transparent;
}

/* Intro Text */
.intro-title {
    font-size: 3.2rem;
    margin-bottom: 16px;
    background: linear-gradient(135deg, #fff 0%, rgba(255,255,255,0.6) 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

.intro-subtitle {
    font-family: var(--re-font-body);
    font-size: 1.25rem;
    color: var(--bs-primary);
    font-weight: 600;
    margin-bottom: 24px;
}

.intro-content {
    font-family: var(--re-font-body);
    font-size: 1.1rem;
    line-height: 1.8;
    color: var(--re-text-color);
    text-shadow: 0 2px 4px rgba(0,0,0,0.3);
}

/* =========================================
   2. CONTENT SECTIONS (Glass Cards)
   ========================================= */
.about-section {
    border-radius: var(--re-radius-lg);
    overflow: hidden;
    position: relative;
    transition: transform 0.3s ease;
}

.about-section.contained {
    background: rgba(20, 15, 22, 0.6);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    border: 1px solid rgba(130, 107, 125, 0.15);
    box-shadow: 0 8px 32px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.05);
    padding: 40px;
}

.about-section.open {
    background: rgba(20, 15, 22, 0.4);
    backdrop-filter: blur(12px) saturate(160%);
    -webkit-backdrop-filter: blur(12px) saturate(160%);
    border: 1px solid rgba(130, 107, 125, 0.25);
    box-shadow: 0 4px 24px rgba(0,0,0,0.2);
    padding: 40px;
}

.section-separator {
    height: 1px;
    width: 60%;
    margin: 0 auto;
    background: linear-gradient(90deg, transparent 0%, rgba(130,107,125,0.3) 50%, transparent 100%);
}

.section-content {
    font-family: var(--re-font-body);
    font-size: 1.05rem;
    line-height: 1.8;
    color: var(--re-text-color);
}

/* =========================================
   3. SKILLS GRID
   ========================================= */
.skills-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 24px;
}

.skill-category {
    background: rgba(20, 15, 22, 0.6);
    border: 1px solid rgba(130, 107, 125, 0.2);
    border-radius: var(--re-radius-lg);
    padding: 24px;
    backdrop-filter: blur(16px) saturate(180%);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.02);
}

.skill-category-title {
    display: flex;
    align-items: center;
    gap: 12px;
    color: var(--bs-primary);
    margin-bottom: 20px;
    font-size: 1.6rem !important; /* Forces retro size */
}

.skill-category-title i {
    font-size: 1.2rem;
}

.skill-item {
    margin-bottom: 16px;
}

.skill-item:last-child {
    margin-bottom: 0;
}

.skill-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 8px;
    font-family: var(--re-font-body);
    font-size: 0.95rem;
    color: rgba(255,255,255,0.9);
}

.skill-name {
    display: flex;
    align-items: center;
    gap: 8px;
}

.skill-name i {
    color: var(--bs-primary);
    opacity: 0.8;
}

.skill-percent {
    font-family: var(--re-font-ui);
    font-size: 8px;
    color: var(--bs-primary);
}

.skill-progress-track {
    height: 6px;
    background: rgba(0, 0, 0, 0.4);
    border-radius: var(--re-radius-sm);
    overflow: hidden;
    box-shadow: inset 0 1px 3px rgba(0,0,0,0.5);
}

.skill-progress-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--re-brand-400) 0%, var(--bs-primary) 100%);
    border-radius: var(--re-radius-sm);
    box-shadow: 0 0 8px rgba(var(--bs-primary-rgb), 0.5);
    transition: width 1s ease-out;
}

/* =========================================
   4. EXPERIENCE TIMELINE
   ========================================= */
.experience-grid {
    display: flex;
    flex-direction: column;
    gap: 32px;
    position: relative;
    padding-left: 0;
}

.experience-item {
    background: rgba(20, 15, 22, 0.6);
    border: 1px solid rgba(130, 107, 125, 0.2);
    border-radius: var(--re-radius-lg);
    padding: 24px;
    position: relative;
    backdrop-filter: blur(16px) saturate(180%);
    transition: transform 0.3s ease;
}

.experience-item:hover {
    transform: translateX(8px);
    background: rgba(20, 15, 22, 0.8);
    border-color: rgba(130, 107, 125, 0.4);
}

.experience-header {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 12px;
}

.experience-icon {
    width: 48px;
    height: 48px;
    border-radius: var(--re-radius-lg);
    background: rgba(0,0,0,0.3);
    border: 1px solid rgba(130,107,125,0.3);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    color: var(--bs-primary);
    box-shadow: inset 0 2px 4px rgba(255,255,255,0.05);
}

.experience-title {
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--bs-heading-color);
    margin-bottom: 4px;
}

.experience-company {
    font-family: var(--re-font-body);
    font-size: 1rem;
    color: var(--re-text-color);
}

.experience-location {
    opacity: 0.6;
    font-size: 0.9rem;
}

.experience-date {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-family: var(--re-font-ui);
    font-size: 8px;
    padding: 6px 12px;
    background: rgba(0,0,0,0.4);
    border-radius: var(--re-radius-sm);
    border: 1px solid rgba(130,107,125,0.2);
    margin-bottom: 16px;
    color: var(--bs-link-color);
}

.experience-current-badge {
    background: rgba(40,167,69,0.2);
    color: #28a745;
    border: 1px solid rgba(40,167,69,0.4);
    padding: 2px 6px;
    border-radius: var(--re-radius-sm);
    font-size: 8px;
}

.experience-description {
    font-family: var(--re-font-body);
    font-size: 1rem;
    line-height: 1.6;
    color: var(--re-text-color);
    margin-bottom: 20px;
}

.experience-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.experience-tag {
    font-family: var(--re-font-ui);
    font-size: 8px;
    text-transform: uppercase;
    padding: 4px 10px;
    background: rgba(130,107,125,0.15);
    border: 1px solid rgba(130,107,125,0.2);
    border-radius: var(--re-radius-sm);
    color: var(--re-text-color);
}

/* =========================================
   5. GAMING CAROUSELS
   ========================================= */
.gaming-section {
    margin-bottom: 60px;
    position: relative;
}

.section-title {
    margin: 0;
    font-size: 2.2rem;
    color: var(--bs-heading-color);
    font-weight: 700;
}

.section-subtitle {
    font-family: var(--re-font-body);
    font-size: 1.25rem;
    color: var(--bs-primary);
    margin-bottom: 24px;
}

.section-header-accent {
    display: flex;
    align-items: center;
    gap: 20px;
    margin-bottom: 32px;
}

/*
 * REMOVED: a second .accent-bar definition.
 *
 * This one made the bar 60px x 4px - horizontal - and outranked the 4px x 32px
 * vertical rule in page-standard.css (0,2,0 beats 0,1,0), so the same element
 * rendered as a horizontal dash on the About page and as a vertical strip
 * everywhere else. Both are gone along with the elements themselves.
 */

.section-header-text .section-meta {
    font-family: var(--re-font-body);
    font-size: 1rem;
    color: rgba(255,255,255,0.6);
    margin: 4px 0 0 0;
}

/* Carousel Container */
.games-carousel-container {
    position: relative;
    padding: 0 48px; /* Room for navigation buttons */
}

.games-carousel {
    display: flex;
    gap: var(--re-space-5);
    overflow-x: auto;
    overflow-y: hidden;
    padding: var(--re-space-4) var(--re-space-1);
    scroll-behavior: smooth;
    scroll-snap-type: x mandatory;
    /* Snapped cards land clear of the arrows rather than under them. */
    scroll-padding-inline: var(--re-space-1);
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* IE/Edge */
}

.games-carousel::-webkit-scrollbar {
    display: none; /* Chrome/Safari */
}

/* ------------------------------------------------------------------
   Carousel navigation

   The two-class selector is deliberate. z-index-layers.css carries a
   `button:not(.dropdown-toggle)` rule at specificity (0,1,1) that sets
   `position: relative` on every button; a single-class `.carousel-prev`
   rule loses to it and these arrows drop into normal flow, stacking up
   at the bottom-left of the section. That rule now skips `-prev`/`-next`
   buttons, and this selector wins on its own regardless.
   ------------------------------------------------------------------ */
.games-carousel-container > .carousel-prev,
.games-carousel-container > .carousel-next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    border-radius: var(--re-radius-pill);
    background: var(--re-surface);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    border: 1px solid var(--re-border);
    color: var(--re-text-color);
    font-size: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 4;
    transition: background var(--re-transition), border-color var(--re-transition),
                opacity var(--re-transition), transform var(--re-transition);
    box-shadow: var(--re-shadow-2);
}

.games-carousel-container > .carousel-prev { left: 0; }
.games-carousel-container > .carousel-next { right: 0; }

.games-carousel-container > .carousel-prev:hover,
.games-carousel-container > .carousel-next:hover {
    background: var(--re-brand-600);
    border-color: var(--re-border-strong);
    color: #ffffff;
    transform: translateY(-50%) scale(1.08);
}

.games-carousel-container > .carousel-prev:focus-visible,
.games-carousel-container > .carousel-next:focus-visible {
    outline: 2px solid var(--re-brand-200);
    outline-offset: 3px;
}

/* Set by the controller when the carousel is at one end, or not scrollable. */
.games-carousel-container > .carousel-prev[hidden],
.games-carousel-container > .carousel-next[hidden] {
    display: none;
}

/* Fade the cards out under the arrows instead of letting them run to a hard
   cut, so it reads as "there is more" rather than as clipping. */
.games-carousel-container::before,
.games-carousel-container::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 48px;
    pointer-events: none;
    z-index: 3;
}

.games-carousel-container::before {
    left: 40px;
    background: linear-gradient(90deg, var(--re-ground-outer) 0%, transparent 100%);
    opacity: 0.85;
}

.games-carousel-container::after {
    right: 40px;
    background: linear-gradient(270deg, var(--re-ground-outer) 0%, transparent 100%);
    opacity: 0.85;
}

/* Game Card */
.game-card {
    flex: 0 0 280px;
    background: rgba(20, 15, 22, 0.6);
    backdrop-filter: blur(16px) saturate(180%);
    border-radius: var(--re-radius-lg);
    border: 1px solid rgba(130,107,125,0.2);
    overflow: hidden;
    scroll-snap-align: start;
    box-shadow: 0 8px 24px rgba(0,0,0,0.4);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    display: flex;
    flex-direction: column;
}

.game-card:hover {
    transform: translateY(-8px);
    box-shadow: 0 16px 32px rgba(0,0,0,0.6);
    border-color: rgba(130,107,125,0.5);
}

/* ------------------------------------------------------------------
   Cover art

   Every game's art is stored as a 2:3 portrait (600x900), so the media
   box is 2:3 and the art fills it edge-to-edge with object-fit: cover.
   Because the box matches the source ratio, nothing is cropped and no
   letterbox / pillar bars appear.

   (This replaces an earlier scheme that fixed the box at 3:4 and
   contain'd the art over a blurred copy of itself. With the art actually
   2:3, that left a grey blurred bar down each side of every card - the
   pillarboxing this removes. The blurred .art-backdrop image it needed
   is gone from the markup too.)
   ------------------------------------------------------------------ */
.game-card .media {
    position: relative;
    width: 100%;
    aspect-ratio: 2 / 3;
    overflow: hidden;
    background: var(--re-surface-sunken);
}

.game-card .media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s ease;
}

.game-card:hover .media img {
    transform: scale(1.04);
}

.game-card .status-badge,
.game-card .rating-badge {
    position: absolute;
    bottom: 12px;
    right: 12px;
    padding: 6px 12px;
    border-radius: var(--re-radius-pill);
    font-family: var(--re-font-ui);
    font-size: 8px;
    color: #fff;
    text-transform: uppercase;
    backdrop-filter: blur(4px);
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}

.game-card .content {
    padding: 20px;
    display: flex;
    flex-direction: column;
    flex: 1;
}

.game-card .title {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--bs-heading-color);
    margin-bottom: 12px;
    line-height: 1.3;
}

.game-card .tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 16px;
}

.game-card .tags span {
    font-family: var(--re-font-ui);
    font-size: 8px;
    padding: 4px 8px;
    background: rgba(130,107,125,0.15);
    border: 1px solid rgba(130,107,125,0.2);
    border-radius: var(--re-radius-sm);
    color: var(--re-text-color);
    text-transform: uppercase;
}

.game-card .progress {
    margin-bottom: 16px;
    background: transparent;
}

.game-card .progress .row {
    display: flex;
    justify-content: space-between;
    margin-bottom: 6px;
    font-family: var(--re-font-ui);
    font-size: 8px;
    color: var(--bs-link-color);
}

.game-card .progress .track {
    height: 4px;
    background: rgba(0,0,0,0.5);
    border-radius: var(--re-radius-xs);
    overflow: hidden;
}

.game-card .progress .fill {
    height: 100%;
    background: var(--bs-primary);
    border-radius: var(--re-radius-xs);
}

.game-card .info {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--re-font-body);
    font-size: 0.9rem;
    color: rgba(255,255,255,0.6);
    margin-bottom: 12px;
}

.game-card .note {
    font-family: var(--re-font-body);
    font-size: 0.9rem;
    font-style: italic;
    color: rgba(255,255,255,0.8);
    background: rgba(0,0,0,0.2);
    padding: 12px;
    border-radius: var(--re-radius-md);
    margin-top: auto;
    border-left: 2px solid var(--bs-primary);
}

/* Favorite Games variations */
.game-card.favorite .media {
    aspect-ratio: 2 / 3; /* Same 2:3 box-art ratio as the source images */
}

.game-card.favorite .why-favorite,
.game-card.favorite .memorable-moment {
    margin-top: 16px;
}

.game-card.favorite .label {
    font-family: var(--re-font-ui);
    font-size: 8px;
    color: #f39c12;
    text-transform: uppercase;
    margin-bottom: 6px;
    display: flex;
    align-items: center;
    gap: 6px;
}

/* =========================================
   6. RESPONSIVE ADJUSTMENTS
   ========================================= */
@media (max-width: 991px) {
    .about-hero-container {
        padding: 32px 20px;
    }
    
    .profile-image {
        width: 160px;
        height: 160px;
    }
    
    .intro-title {
        font-size: 2.4rem;
        text-align: center;
        margin-top: 24px;
    }
    
    .intro-subtitle {
        text-align: center;
    }
}

@media (max-width: 768px) {
    .games-carousel-container {
        padding: 0;
    }

    /* Touch scrolling is the better affordance here, and there is no room for
       arrows without covering a card. The selector has to match the
       specificity of the desktop rule above (two classes) or it loses. */
    .games-carousel-container > .carousel-prev,
    .games-carousel-container > .carousel-next {
        display: none;
    }

    /* The edge fades are anchored to the 48px arrow gutter, which is gone. */
    .games-carousel-container::before,
    .games-carousel-container::after {
        content: none;
    }

    /* Narrower cards so more than one is visible on a phone. */
    .game-card {
        flex-basis: 240px;
    }
    
    .about-section.contained,
    .about-section.open {
        padding: 24px;
    }
    
    .experience-grid {
        padding-left: 0;
    }
}


/* ===== assets/css/about-gaming-carousels.css ===== */
/*
 * Gaming carousel aspect overrides
 * ================================
 * The default media box is 3/4 (box-art shaped) and is declared in
 * about-page.css, which also letterboxes the art inside it over a blurred
 * copy - so art of any shape fits without being cropped and every card in a
 * row stays the same height.
 *
 * These data-aspect hooks remain for the case where a whole section is
 * deliberately one non-default shape (an all-key-art row, say). Nothing sets
 * the attribute today: renderGameCard() in templates/about.php has never
 * emitted it, which is why the "native aspect ratios" note at the top of that
 * file described a feature that was not wired up. Set data-aspect on the
 * .game-card to use one.
 */

.game-card[data-aspect='2-3']  .media { aspect-ratio: 2 / 3; }
.game-card[data-aspect='3-4']  .media { aspect-ratio: 3 / 4; }
.game-card[data-aspect='16-9'] .media { aspect-ratio: 16 / 9; }
.game-card[data-aspect='1-1']  .media { aspect-ratio: 1 / 1; }

/* Drag-to-scroll affordance. */
.games-carousel { cursor: grab; }
.games-carousel:active { cursor: grabbing; }


/* ===== assets/css/projects-page.css ===== */
/**
 * Projects Page Styles
 * Uses GLASS STANDARD for consistent styling
 * Matches music player refractive glass with rim lighting
 * Supports light/dark theme via CSS variables
 */

/* ============================================
   PROJECTS HEADER
   ============================================ */

.projects-header > div {
  background: var(--glass-bg, rgba(0, 0, 0, 0.15)) !important;
  backdrop-filter: var(--glass-blur, blur(16px) saturate(180%) contrast(110%)) !important;
  -webkit-backdrop-filter: var(--glass-blur, blur(16px) saturate(180%) contrast(110%)) !important;
  border: 1px solid var(--glass-border, rgba(0, 0, 0, 0.25)) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow, 
    0 8px 32px 0 rgba(0, 0, 0, 0.5),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.15),
    inset 0 -1px 0 0 rgba(0, 0, 0, 0.15)) !important;
  position: relative;
  overflow: hidden;
}

/* Noise texture for header */
.projects-header > div::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseProjectsHeader'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseProjectsHeader)'/%3E%3C/svg%3E");
  opacity: 0.06;
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

/* Rim light on top edge */
.projects-header > div::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
  pointer-events: none;
  z-index: 2;
}

.projects-header > div > * {
  position: relative;
  z-index: 3;
}

/* ============================================
   FILTER & SEARCH BAR
   ============================================ */
/*
 * NOTE: the [style*="..."] selectors below match elements by the text of their
 * inline style attribute. That is brittle - the value they key on changed once
 * already when the radii were tokenised, which silently unstyled every panel
 * they were holding up. The .projects-filter-bar class beside them is never
 * applied to any markup; give these elements a real class and delete the
 * attribute selectors when there is time.
 */

.projects-filter-bar,
[style*="padding: 20px"][style*="border-radius: var(--re-radius-lg)"] {
  background: var(--glass-bg, rgba(0, 0, 0, 0.15)) !important;
  backdrop-filter: var(--glass-blur, blur(16px) saturate(180%) contrast(110%)) !important;
  -webkit-backdrop-filter: var(--glass-blur, blur(16px) saturate(180%) contrast(110%)) !important;
  border: 1px solid var(--glass-border, rgba(0, 0, 0, 0.25)) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow, 
    0 8px 32px 0 rgba(0, 0, 0, 0.5),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.15),
    inset 0 -1px 0 0 rgba(0, 0, 0, 0.15)) !important;
  position: relative;
  overflow: hidden;
}

/* Noise texture for filter bar */
[style*="padding: 20px"][style*="border-radius: var(--re-radius-lg)"]::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseProjectsFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseProjectsFilter)'/%3E%3C/svg%3E");
  opacity: 0.06;
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

/* Rim light on top edge */
[style*="padding: 20px"][style*="border-radius: var(--re-radius-lg)"]::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
  pointer-events: none;
  z-index: 2;
}

/* Content above effects */
[style*="padding: 20px"][style*="border-radius: var(--re-radius-lg)"] > * {
  position: relative;
  z-index: 3;
}

/* ============================================
   PROJECT CARDS - GLASS STANDARD
   ============================================ */

.project-card {
  background: var(--glass-bg, rgba(0, 0, 0, 0.15)) !important;
  backdrop-filter: var(--glass-blur, blur(16px) saturate(180%) contrast(110%)) !important;
  -webkit-backdrop-filter: var(--glass-blur, blur(16px) saturate(180%) contrast(110%)) !important;
  border: 1px solid var(--glass-border, rgba(0, 0, 0, 0.25)) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow, 
    0 8px 32px 0 rgba(0, 0, 0, 0.5),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.15),
    inset 0 -1px 0 0 rgba(0, 0, 0, 0.15)) !important;
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}

/* Noise texture for project cards */
.project-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseProjectCard'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseProjectCard)'/%3E%3C/svg%3E");
  opacity: 0.06;
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

/* Rim light on top edge */
.project-card::after {
  content: '';
  position: absolute;
  top: 1px;
  left: 8px;
  right: 8px;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
  pointer-events: none;
  z-index: 2;
}

.project-card > * {
  position: relative;
  z-index: 3;
}

.project-card:hover {
  background: var(--glass-bg-hover, rgba(130, 107, 125, 0.25)) !important;
  border-color: var(--glass-border-hover, rgba(155, 89, 182, 0.5)) !important;
  transform: translateY(-8px);
  box-shadow: var(--glass-shadow-hover, 
    0 12px 40px 0 rgba(130, 107, 125, 0.6),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.25),
    inset 0 -1px 0 0 rgba(0, 0, 0, 0.15)) !important;
}

/* Image container gradient overlay */
.project-card > div:first-child {
  position: relative;
  z-index: 3;
}

/* ============================================
   EMPTY STATE
   ============================================ */

.projects-empty-state {
  background: var(--glass-bg-dark, rgba(0, 0, 0, 0.2)) !important;
  backdrop-filter: var(--glass-blur, blur(16px) saturate(180%) contrast(110%)) !important;
  -webkit-backdrop-filter: var(--glass-blur, blur(16px) saturate(180%) contrast(110%)) !important;
  border: 1px solid var(--glass-border, rgba(0, 0, 0, 0.15)) !important;
  border-radius: var(--re-radius-lg) !important;
  box-shadow: var(--glass-shadow, 
    0 8px 32px 0 rgba(0, 0, 0, 0.5),
    inset 0 1px 0 0 rgba(255, 255, 255, 0.1),
    inset 0 -1px 0 0 rgba(0, 0, 0, 0.2)) !important;
  position: relative;
  overflow: hidden;
}

/* Noise for empty state */
[style*="padding: 80px 20px"]::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseProjectsEmpty'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseProjectsEmpty)'/%3E%3C/svg%3E");
  opacity: 0.04;
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}

[style*="padding: 80px 20px"] > * {
  position: relative;
  z-index: 2;
}

/* ============================================
   PAGINATION - GLASS STANDARD
   ============================================ */

.pagination a {
  background: var(--glass-bg, rgba(0, 0, 0, 0.15)) !important;
  backdrop-filter: var(--re-glass-subtle) !important;
  -webkit-backdrop-filter: var(--re-glass-subtle) !important;
  border: 1px solid var(--glass-border, rgba(0, 0, 0, 0.15)) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: 
    0 2px 8px rgba(0,0,0,0.3),
    inset 0 1px 0 0 rgba(255,255,255,0.1) !important;
  transition: all 0.3s ease;
}

.pagination a:hover {
  background: var(--re-surface-hover) !important;
  border-color: var(--re-border-strong) !important;
  transform: translateY(-2px);
  box-shadow: 
    0 4px 12px rgba(130,107,125,0.5),
    inset 0 1px 0 0 rgba(255,255,255,0.15) !important;
}

/* Active page button */
.pagination a[style*="background: var(--re-brand-600)"] {
  background: rgba(130,107,125,0.5) !important;
  border-color: var(--re-border-strong) !important;
  box-shadow: 
    0 4px 12px rgba(130,107,125,0.6),
    inset 0 1px 0 0 rgba(255,255,255,0.2) !important;
}

/* ============================================
   FILTER BUTTONS - GLASS STANDARD
   ============================================ */

/* Type filter buttons */
[style*="padding: 8px 16px"][href*="type="] {
  background: var(--glass-bg, rgba(0, 0, 0, 0.15)) !important;
  backdrop-filter: var(--re-glass-subtle) !important;
  -webkit-backdrop-filter: var(--re-glass-subtle) !important;
  border: 1px solid var(--glass-border, rgba(0, 0, 0, 0.15)) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: 
    0 2px 8px rgba(0,0,0,0.3),
    inset 0 1px 0 0 rgba(255,255,255,0.1) !important;
  transition: all 0.3s ease;
}

/* Active filter button */
[style*="padding: 8px 16px"][style*="rgba(130,107,125,0.4)"] {
  background: rgba(130,107,125,0.4) !important;
  border-color: var(--re-border-strong) !important;
  box-shadow: 
    0 4px 12px rgba(130,107,125,0.6),
    inset 0 1px 0 0 rgba(255,255,255,0.2) !important;
}

/* ============================================
   VIEW TOGGLE BUTTONS - GLASS STANDARD
   ============================================ */

.view-btn {
  background: var(--glass-bg, rgba(0, 0, 0, 0.15)) !important;
  backdrop-filter: var(--re-glass-subtle) !important;
  -webkit-backdrop-filter: var(--re-glass-subtle) !important;
  border: 1px solid var(--glass-border, rgba(0, 0, 0, 0.15)) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: 
    0 2px 8px rgba(0,0,0,0.3),
    inset 0 1px 0 0 rgba(255,255,255,0.1) !important;
  transition: all 0.3s ease;
}

.view-btn:hover {
  background: var(--re-surface-hover) !important;
  border-color: var(--re-border-strong) !important;
  transform: translateY(-2px);
}

.view-btn.active {
  background: var(--re-surface-active) !important;
  border-color: var(--re-border-strong) !important;
  box-shadow: 
    0 4px 12px rgba(130,107,125,0.6),
    inset 0 1px 0 0 rgba(255,255,255,0.2) !important;
}

/* ============================================
   PROJECT BADGES - GLASS STANDARD
   ============================================ */

/* Type badge */
[style*="position: absolute"][style*="top: 12px"][style*="right: 12px"] > div {
  background: rgba(0, 0, 0, 0.8) !important;
  backdrop-filter: var(--re-glass) !important;
  -webkit-backdrop-filter: var(--re-glass) !important;
  border: 1px solid rgba(0, 0, 0, 0.15) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: 
    0 4px 16px rgba(0,0,0,0.6),
    inset 0 1px 0 0 rgba(255,255,255,0.1) !important;
}

/* Date badge */
[style*="position: absolute"][style*="top: 12px"][style*="left: 12px"] > div {
  background: rgba(0, 0, 0, 0.8) !important;
  backdrop-filter: var(--re-glass) !important;
  -webkit-backdrop-filter: var(--re-glass) !important;
  border: 1px solid rgba(0, 0, 0, 0.15) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: 
    0 4px 16px rgba(0,0,0,0.6),
    inset 0 1px 0 0 rgba(255,255,255,0.1) !important;
}

/* ============================================
   SEARCH INPUT - GLASS STANDARD
   ============================================ */

input[type="text"][placeholder*="Search"] {
  background: var(--re-surface) !important;
  backdrop-filter: var(--re-glass-subtle) !important;
  -webkit-backdrop-filter: var(--re-glass-subtle) !important;
  border: 1px solid var(--glass-border, rgba(0, 0, 0, 0.15)) !important;
  border-radius: var(--re-radius-md) !important;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3) !important;
}

input[type="text"][placeholder*="Search"]:focus {
  background: var(--re-surface-raised) !important;
  border-color: var(--re-border-strong) !important;
  box-shadow: 
    0 0 0 3px rgba(130, 107, 125, 0.25),
    inset 0 1px 2px rgba(0, 0, 0, 0.2) !important;
}

/* ============================================
   SELECT DROPDOWN - GLASS STANDARD
   ============================================ */

/*
 * The sort <select> is styled in page-refined.css now, with the rest of the
 * filter row. What stood here was a bare select[name="sort"] carrying six
 * !important declarations - unscoped, so it reached any sort control
 * anywhere on the site, and unarguable-with from a class selector. The
 * markup it was written for (.filter-bar) no longer exists.
 */

/* ============================================
   RESPONSIVE
   ============================================ */

@media (max-width: 768px) {
  .projects-header > div {
    padding: 24px 20px !important;
    backdrop-filter: blur(12px) saturate(150%) contrast(105%) !important;
    -webkit-backdrop-filter: blur(12px) saturate(150%) contrast(105%) !important;
  }
  
  /* Reduce noise opacity on mobile for performance */
  .projects-header > div::before,
  [style*="padding: 20px"][style*="border-radius: var(--re-radius-lg)"]::before,
  .project-card::before,
  [style*="padding: 80px 20px"]::before {
    opacity: 0.03;
  }
  
  .projects-grid {
    grid-template-columns: 1fr !important;
    gap: 16px !important;
  }
  
  .project-card {
    /* Simpler shadows for mobile performance */
    box-shadow: 
      0 4px 16px 0 rgba(0, 0, 0, 0.4),
      inset 0 1px 0 0 rgba(255, 255, 255, 0.1) !important;
  }
  
  .project-card:hover {
    box-shadow: 
      0 8px 24px 0 rgba(0, 0, 0, 0.5),
      inset 0 1px 0 0 rgba(255, 255, 255, 0.15) !important;
  }
}

/* ============================================
   LIST VIEW STYLES
   ============================================ */

.projects-list .project-card {
  display: flex !important;
  flex-direction: row !important;
  height: auto !important;
}

.projects-list .project-card > div:first-child {
  width: 280px !important;
  height: auto !important;
  min-height: 200px !important;
}

.projects-list .project-card > div:last-child {
  flex: 1 !important;
}

/* ============================================
   REDUCED MOTION
   ============================================ */

@media (prefers-reduced-motion: reduce) {
  .project-card,
  .view-btn,
  .pagination a,
  input[type="text"],
  select {
    transition: none !important;
  }
  
  .project-card:hover {
    transform: none !important;
  }
}


/* ===== assets/css/Profile-Card.css ===== */
.profile-card {
  border-radius: var(--re-radius-sm);
}

.profile-card-img {
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
}



/* ===== assets/css/contact-page-fixes.css ===== */
/**
 * Contact Page Fixes
 * Fixes dark text on dark backgrounds and oversized Silkscreen fonts
 */

/* ============================================
   CONTACT PAGE CONTAINER
   ============================================ */

/* Override inline styles with proper theme colors */
.container.py-4.py-xl-5 h2,
.container.py-4.py-xl-5 p,
.container.py-4.py-xl-5 label {
    color: var(--re-text-color) !important; /* Light text, not dark */
}

/* ============================================
   FORM LABELS - LIGHT TEXT ON DARK BG
   ============================================ */

/* All labels should be light colored */
label[for="contact-name"],
label[for="contact-email"],
label[for="contact-subject"],
label[for="contact-message"],
.form-label {
    color: var(--re-text-color) !important;
    font-family: var(--re-font-body) !important;
    font-size: 0.9rem !important;
    margin-bottom: 8px !important;
    display: block !important;
    text-align: left !important;
}

/* Label helper text */
label span[style*="opacity"] {
    opacity: 0.6 !important;
    font-size: 0.8rem !important;
    color: var(--re-text-color) !important;
}

/* ============================================
   FORM INPUTS - LIGHT TEXT
   ============================================ */

/* All form inputs should have light text */
#contact-name,
#contact-email,
#contact-subject,
#contact-message,
.form-control {
    background: var(--re-surface) !important;
    border: 1px solid var(--re-brand-400) !important;
    color: var(--re-text-color) !important; /* Light text */
    padding: 12px !important;
    border-radius: var(--re-radius-md) !important;
    font-family: var(--re-font-body) !important;
    font-size: 14px !important;
}

/* Readonly inputs (name, email) */
#contact-name[readonly],
#contact-email[readonly] {
    background: var(--re-surface) !important;
    opacity: 0.8 !important;
    cursor: not-allowed !important;
}

/* Input focus state */
.form-control:focus {
    outline: none !important;
    border-color: var(--re-brand-400) !important;
    box-shadow: 0 0 0 3px rgba(130, 107, 125, 0.25) !important;
    background: var(--re-surface) !important;
    color: var(--re-text-color) !important;
}

/* Placeholder text */
.form-control::placeholder {
    color: rgba(248, 249, 250, 0.5) !important;
}

/* Textarea */
textarea.form-control {
    min-height: 150px !important;
    resize: vertical !important;
}

/* ============================================
   SUBMIT BUTTON - SILKSCREEN 8PX MAX
   ============================================ */

/* Button with Silkscreen font MUST be 8px */
/* Scoped to the contact page. This block used to apply to every submit button
   and every .btn-primary on the site from a file named "contact-page-fixes",
   which is why the primary button could not be changed anywhere else. The
   site-wide definition now lives in theme-color-fix.css under UNIFIED CONTROLS. */
body[data-page="contact"] button[type="submit"],
body[data-page="contact"] .btn-primary {
    background: var(--re-brand-600) !important;
    border: none !important;
    padding: 12px 30px !important;
    border-radius: var(--re-radius-md) !important;
    font-family: var(--re-font-ui) !important;
    font-size: 8px !important; /* CRITICAL: Max 8px for Silkscreen */
    text-transform: uppercase !important;
    letter-spacing: 1px !important;
    font-weight: 600 !important;
    color: var(--re-text-color) !important;
    transition: all 0.3s ease !important;
    display: inline-flex !important;
    align-items: center !important;
    gap: 8px !important;
    cursor: pointer !important;
}

button[type="submit"]:hover,
.btn-primary:hover {
    background: var(--re-brand-400) !important;
    transform: translateY(-2px) !important;
    box-shadow: 0 4px 12px rgba(130, 107, 125, 0.4) !important;
    color: var(--re-text-color) !important;
}

/* Button icon sizing */
button[type="submit"] i,
.btn-primary i {
    font-size: 12px !important; /* Icon slightly larger than text */
    line-height: 1 !important;
}

/* ============================================
   HEADINGS & TEXT - LIGHT ON DARK
   ============================================ */

/* Page title */
.container.py-4.py-xl-5 h2 {
    color: var(--re-brand-400) !important;
    font-family: var(--re-font-body) !important;
    text-shadow: 0px 1px rgba(0,0,0,0.5) !important;
}

/* Subtitle "Get in touch" */
.container.py-4.py-xl-5 p.w-lg-50 {
    color: var(--re-text-color) !important; /* Light text */
    text-shadow: 0px 1px rgba(0,0,0,0.5) !important;
}

/* ============================================
   ALERT MESSAGES
   ============================================ */

/* Logged in alert */
.alert.alert-info {
    background: var(--re-surface-raised) !important;
    border: 1px solid var(--re-border-strong) !important;
    color: var(--re-text-color) !important; /* Light text */
    text-align: left !important;
    margin-bottom: 20px !important;
}

.alert.alert-info strong {
    color: var(--re-text-color) !important;
}

.alert.alert-info i {
    color: var(--re-brand-400) !important;
}

/* Success alert */
.alert.alert-success {
    background: rgba(40, 167, 69, 0.2) !important;
    border: 1px solid rgba(40, 167, 69, 0.5) !important;
    color: var(--re-text-color) !important;
}

/* Error alert */
.alert.alert-danger {
    background: rgba(220, 53, 69, 0.2) !important;
    border: 1px solid rgba(220, 53, 69, 0.5) !important;
    color: var(--re-text-color) !important;
}

/* ============================================
   LOGIN REQUIRED MESSAGE
   ============================================ */

/* Guest login required box */
.alert[role="alert"] h4 {
    color: var(--re-text-color) !important;
    font-family: var(--re-font-body) !important;
}

.alert[role="alert"] p {
    color: var(--re-text-color) !important;
    opacity: 0.9 !important;
}

/* Login/Register buttons */
.alert[role="alert"] .btn {
    font-family: var(--re-font-body) !important;
    font-size: 14px !important; /* NOT Silkscreen for these buttons */
    font-weight: 600 !important;
}

/* ============================================
   HR DIVIDER
   ============================================ */

/* Contact page HR should use gradient */
.container.py-4.py-xl-5 hr {
    border: none !important;
    height: 1px !important;
    background: linear-gradient(
        to right,
        transparent 0%,
        rgba(130, 107, 125, 0.3) 20%,
        rgba(130, 107, 125, 0.6) 50%,
        rgba(130, 107, 125, 0.3) 80%,
        transparent 100%
    ) !important;
    margin: 20px 0 !important;
}

/* ============================================
   AUTOCOMPLETE STYLING
   ============================================ */

/* Fix autofill styling */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--re-text-color) !important;
    -webkit-box-shadow: 0 0 0px 1000px rgba(0,0,0,0.3) inset !important;
    transition: background-color 5000s ease-in-out 0s !important;
}

/* ============================================
   RESPONSIVE - MOBILE
   ============================================ */

@media (max-width: 768px) {
    /* Smaller padding on mobile */
    .container.py-4.py-xl-5 {
        padding-left: 20px !important;
        padding-right: 20px !important;
    }
    
    /* Adjust font sizes */
    label {
        font-size: 0.85rem !important;
    }
    
    .form-control {
        font-size: 16px !important; /* Prevents zoom on iOS */
    }
    
    /* Button */
    button[type="submit"],
    .btn-primary {
        padding: 10px 24px !important;
        font-size: 8px !important; /* Still 8px for Silkscreen */
    }
}

/* ============================================
   ACCESSIBILITY
   ============================================ */

/* High contrast mode */
@media (prefers-contrast: high) {
    label,
    .form-control,
    button[type="submit"] {
        border-width: 2px !important;
    }
    
    .form-control {
        background: rgba(0,0,0,0.5) !important;
    }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
    button[type="submit"],
    .btn-primary {
        transition: none !important;
    }
}

/* ============================================
   PRINT STYLES
   ============================================ */

@media print {
    .container.py-4.py-xl-5 {
        background: white !important;
        box-shadow: none !important;
    }
    
    label,
    .form-control {
        color: #000 !important;
        border-color: #000 !important;
    }
    
    button[type="submit"] {
        display: none !important;
    }
}


/* ===== assets/css/analytics-world-map.css ===== */
/**
 * Analytics World Map Styles
 * Real-time visitor geolocation visualization
 */

/* ============================================
   MAP CONTAINER - STRICT CONTAINMENT
   ============================================ */

/*
 * A floor under the map.
 *
 * The height here comes from aspect-ratio, which needs a resolved width to
 * compute against. When anything upstream leaves that width at zero - a
 * collapsed flex or grid parent, a hidden ancestor measured too early - the
 * ratio yields no height and the map renders as an empty panel with nothing
 * in the console to explain it. A min-height means the worst case is a map
 * that is the wrong shape rather than a map that is not there.
 */
#analytics-world-map {
  min-height: 320px;
}

/* The map's own report, when it has drawn into nothing. Deliberately plain
   and unmissable - it exists to be read and screenshotted, not to blend in. */
.analytics-map-diagnostic {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid rgba(224, 106, 90, 0.55);
  border-left-width: 3px;
  border-radius: var(--re-radius-sm, 6px);
  background: rgba(224, 106, 90, 0.12);
  color: #ffb4a8;
  font-size: 0.82rem;
  line-height: 1.5;
}

.analytics-world-map-wrapper {
  position: relative;
  width: 100%;
  aspect-ratio: 2000 / 857;
  min-height: 320px;
  max-height: 600px;
  background: var(--re-surface);
  border-radius: var(--re-radius-md);
  overflow: hidden !important; /* CRITICAL: Prevent map from escaping */
  z-index: 1;
}

.analytics-world-map-wrapper svg {
  width: 100% !important;
  height: 100% !important;
  display: block;
  position: relative;
  z-index: 1;
  transform-origin: center center;
}

/* SVG Map Styling */
#world-map {
    pointer-events: none;
    position: relative;
    z-index: 1;
}

#world-map path,
#world-map polygon,
#world-map circle,
#world-map rect {
    transition: fill-opacity 0.3s ease;
    pointer-events: auto;
}

#world-map path:hover,
#world-map polygon:hover {
    fill-opacity: 0.6 !important;
    cursor: default;
}

/* Grid lines should be subtle */
.grid-line {
    pointer-events: none;
}

/* Dots layer */
#visitor-dots {
    z-index: 2;
}

/* Fallback for browsers that don't support aspect-ratio */
@supports not (aspect-ratio: 2000 / 857) {
    .analytics-world-map-wrapper {
        min-height: 400px;
        max-height: 600px;
        height: 42.85vw;
    }
}

/* ============================================
   VISITOR DOTS & ANIMATIONS
   ============================================ */

.visitor-dot {
  cursor: pointer;
  transition: r 0.3s ease, fill 0.3s ease;
  filter: drop-shadow(0 0 4px currentColor);
}

.visitor-dot:hover {
  r: 8 !important;
  filter: drop-shadow(0 0 8px currentColor);
}

/* Pulse ring animation for active visitors */
.pulse-ring {
  fill: none;
  stroke: rgba(40, 167, 69, 0.6);
  stroke-width: 2;
  animation: pulse-ring-expand 2s ease-out infinite;
  pointer-events: none;
}

@keyframes pulse-ring-expand {
  0% {
    r: 6;
    opacity: 1;
    stroke-width: 2;
  }
  100% {
    r: 20;
    opacity: 0;
    stroke-width: 0;
  }
}

/* ============================================
   TOOLTIP
   ============================================ */

#visitor-tooltip {
  position: fixed;
  z-index: 10500 !important;
  background: rgba(0, 0, 0, 0.95);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  color: white;
  padding: 12px 16px;
  border-radius: var(--re-radius-md);
  font-size: 14px;
  font-family: var(--re-font-body);
  pointer-events: none;
  border: 1px solid rgba(255, 255, 255, 0.2);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.8);
  display: none;
  max-width: 300px;
  line-height: 1.5;
}

#visitor-tooltip .active-badge {
  color: #28a745;
  margin-top: 6px;
  font-weight: 700;
}

/* ============================================
   MAP LEGEND - VISIBLE PANEL
   ============================================ */

.map-legend {
    position: absolute;
    bottom: 20px;
    left: 20px;
    background: rgba(0, 0, 0, 0.95);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    padding: 16px 20px;
    border-radius: var(--re-radius-md);
    border: 2px solid rgba(130, 107, 125, 0.6);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.9), inset 0 1px 0 rgba(255,255,255,0.1);
    z-index: 100;
    pointer-events: auto;
    font-family: var(--re-font-ui);
    font-size: 8px;
    color: white;
    min-width: 220px;
}

.map-legend > div {
    display: flex;
    align-items: center;
    gap: 16px;
}

.map-legend > div > div {
    display: flex;
    align-items: center;
    gap: 8px;
}

.map-legend .active-indicator {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #28a745;
    box-shadow: 0 0 12px rgba(40,167,69,0.8);
    animation: pulse-indicator 2s ease-out infinite;
    flex-shrink: 0;
}

.map-legend .recent-indicator {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #6c757d;
    box-shadow: 0 0 6px rgba(108,117,125,0.6);
    flex-shrink: 0;
}

.map-legend span {
    font-family: var(--re-font-ui);
    font-size: 8px;
    color: white;
    white-space: nowrap;
}

/* ============================================
   MAP STATS PANEL - VISIBLE PANEL
   ============================================ */

.map-stats {
    position: absolute;
    top: 20px;
    right: 20px;
    background: rgba(0, 0, 0, 0.95);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    padding: 16px 20px;
    border-radius: var(--re-radius-md);
    border: 2px solid rgba(130, 107, 125, 0.6);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.9), inset 0 1px 0 rgba(255,255,255,0.1);
    z-index: 100;
    pointer-events: auto;
    font-family: var(--re-font-ui);
    font-size: 8px;
    color: white;
    min-width: 160px;
}

.map-stats .stats-header {
    margin-bottom: 12px;
    font-weight: bold;
    color: var(--re-brand-400);
    font-size: 10px;
    font-family: var(--re-font-ui);
    text-transform: uppercase;
    letter-spacing: 1px;
}

.map-stats .stat-row {
    display: flex;
    justify-content: space-between;
    margin-bottom: 8px;
    color: white;
    font-family: var(--re-font-ui);
    font-size: 8px;
}

.map-stats .stat-row:last-child {
    margin-bottom: 0;
}

.map-stats .stat-value {
    font-weight: bold;
    font-family: var(--re-font-ui);
}

#active-count {
    color: #28a745;
}

#today-count {
    color: #17a2b8;
}

#countries-count {
    color: var(--re-brand-400);
}

/* ============================================
   ZOOM CONTROLS - VISIBLE BUTTONS
   ============================================ */

.map-zoom-controls {
    position: absolute;
    bottom: 20px;
    right: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    z-index: 100;
    pointer-events: auto;
}

.map-zoom-controls button {
    width: 44px;
    height: 44px;
    background: rgba(0, 0, 0, 0.95);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border: 2px solid rgba(130, 107, 125, 0.6);
    border-radius: var(--re-radius-md);
    color: white;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    transition: all 0.3s ease;
    pointer-events: auto;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255,255,255,0.1);
}

.map-zoom-controls button:hover {
    background: rgba(130, 107, 125, 0.8);
    border-color: rgba(130, 107, 125, 0.9);
    transform: scale(1.1);
    box-shadow: 0 6px 24px rgba(130,107,125,0.6), inset 0 1px 0 rgba(255,255,255,0.2);
}

.map-zoom-controls button:active {
    transform: scale(0.95);
}

#map-zoom-in i,
#map-zoom-out i,
#map-zoom-reset i {
    font-size: 20px;
}

/* ============================================
   CALIBRATION PANEL
   ============================================ */

#calibration-controls {
    position: absolute;
    top: 20px;
    left: 20px;
    background: rgba(0, 0, 0, 0.95);
    backdrop-filter: blur(16px);
    padding: 16px;
    border-radius: var(--re-radius-md);
    border: 2px solid #ffc107;
    z-index: 100;
    max-width: 360px;
    font-family: var(--re-font-body);
    box-shadow: 0 8px 32px rgba(0,0,0,0.9);
    color: white;
}

/* ============================================
   SMOOTH TRANSITIONS
   ============================================ */

.visitor-group {
    transition: opacity 0.5s ease;
}

.visitor-group.entering {
    animation: fadeInScale 0.5s ease-out;
}

.visitor-group.exiting {
    animation: fadeOutScale 0.5s ease-out forwards;
}

@keyframes fadeInScale {
    0% {
        opacity: 0;
        transform: scale(0);
    }
    100% {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes fadeOutScale {
    0% {
        opacity: 1;
        transform: scale(1);
    }
    100% {
        opacity: 0;
        transform: scale(0);
    }
}

/* Active Visitor Highlight */
.visitor-dot.active {
    fill: #28a745;
    filter: drop-shadow(0 0 4px #28a745);
}

.visitor-dot.inactive {
    fill: #6c757d;
}

/* Glow Effect - stdDeviation is set via SVG attribute in HTML, not CSS */

/* World Map Path Colors */
#world-map path {
    stroke: var(--re-brand-600);
    stroke-width: 1;
}

#world-map line {
    stroke: var(--re-brand-600);
    stroke-width: 0.5;
    opacity: 0.2;
}

/* Map countries hover effect */
.map-country {
    transition: fill-opacity 0.3s ease, stroke 0.3s ease;
}

.map-country:hover {
    fill-opacity: 0.6 !important;
    stroke: var(--re-brand-400) !important;
    stroke-width: 2 !important;
    cursor: pointer;
}


/* ===== assets/css/custom-scrollbar.css ===== */
/**
 * Custom Scrollbar Styling
 * Applies consistent scrollbar design across all scrollable elements
 * Based on recent activity scrollbar design
 */

/* Custom Scrollbar for all scrollable elements */
*::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

*::-webkit-scrollbar-track {
    background: var(--re-surface);
    border-radius: var(--re-radius-sm);
}

*::-webkit-scrollbar-thumb {
    background: linear-gradient(180deg, var(--re-brand-400) 0%, var(--re-brand-600) 100%);
    border-radius: var(--re-radius-sm);
    border: 1px solid var(--re-border);
}

*::-webkit-scrollbar-thumb:hover {
    background: linear-gradient(180deg, var(--re-brand-300) 0%, var(--re-brand-500) 100%);
    border-color: var(--re-border-strong);
}

*::-webkit-scrollbar-thumb:active {
    background: linear-gradient(180deg, var(--re-brand-600) 0%, #4a3c4d 100%);
}

/* Firefox scrollbar styling */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--re-brand-400) rgba(0, 0, 0, 0.2);
}

/* Specific styles for different scroll containers */

/* Admin tables with overflow */
.table-responsive::-webkit-scrollbar {
    height: 8px;
}

/* Admin dashboard metrics panels */
.row .col-md-4 > div[style*="overflow-y: auto"]::-webkit-scrollbar,
.row .col-md-6 > div[style*="overflow-y: auto"]::-webkit-scrollbar,
div[style*="max-height"][style*="overflow-y: auto"]::-webkit-scrollbar {
    width: 8px;
}

/* Recent visitors table scrollbar */
div[style*="max-height: 250px"]::-webkit-scrollbar {
    width: 8px;
}

/* Recent activity scrollbar */
div[style*="max-height: 400px"]::-webkit-scrollbar {
    width: 8px;
}

/* Dropdown menus and modals */
.dropdown-menu::-webkit-scrollbar,
.modal-body::-webkit-scrollbar {
    width: 6px;
}

.dropdown-menu::-webkit-scrollbar-thumb,
.modal-body::-webkit-scrollbar-thumb {
    background: linear-gradient(180deg, var(--re-brand-400) 0%, var(--re-brand-600) 100%);
    border-radius: var(--re-radius-xs);
}

/* Code blocks and pre elements */
pre::-webkit-scrollbar,
code::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

/* Sidebar scrollbars (if any) */
.sidebar::-webkit-scrollbar,
.offcanvas-body::-webkit-scrollbar {
    width: 6px;
}

/* Content area scrollbars */
.content-area::-webkit-scrollbar,
main::-webkit-scrollbar {
    width: 8px;
}

/* Smooth scrolling for better UX */
html {
    scroll-behavior: smooth;
}

/* Hide scrollbar for specific elements if needed */
.no-scrollbar::-webkit-scrollbar {
    display: none;
}

.no-scrollbar {
    -ms-overflow-style: none;
    scrollbar-width: none;
}

/* Scrollbar animations */
*::-webkit-scrollbar-thumb {
    transition: background 0.2s ease, border-color 0.2s ease;
}

/* Custom scrollbar for horizontal scroll */
*::-webkit-scrollbar:horizontal {
    height: 8px;
}

*::-webkit-scrollbar-thumb:horizontal {
    background: linear-gradient(90deg, var(--re-brand-400) 0%, var(--re-brand-600) 100%);
    border-radius: var(--re-radius-sm);
}

*::-webkit-scrollbar-thumb:horizontal:hover {
    background: linear-gradient(90deg, var(--re-brand-300) 0%, var(--re-brand-500) 100%);
}

/* Scrollbar corner (when both scrollbars are visible) */
*::-webkit-scrollbar-corner {
    background: var(--re-surface);
}

/* Better scrollbar for dark backgrounds */
div[style*="background: rgba(0,0,0"]::-webkit-scrollbar-track,
div[style*="background: rgba(130,107,125"]::-webkit-scrollbar-track {
    background: var(--re-surface);
}

/* Scrollbar for light backgrounds */
div[style*="background: rgba(255,255,255"]::-webkit-scrollbar-track,
div[style*="background: #fff"]::-webkit-scrollbar-track,
div[style*="background: white"]::-webkit-scrollbar-track {
 background: var(--re-surface-raised);
}

/* Mobile scrollbar adjustments */
@media (max-width: 768px) {
    *::-webkit-scrollbar {
        width: 6px;
        height: 6px;
    }
    
    *::-webkit-scrollbar-thumb {
        border-width: 0.5px;
    }
}

/* Tablet scrollbar adjustments */
@media (min-width: 769px) and (max-width: 1024px) {
    *::-webkit-scrollbar {
        width: 7px;
     height: 7px;
    }
}

/* Desktop large screens */
@media (min-width: 1920px) {
    *::-webkit-scrollbar {
        width: 10px;
        height: 10px;
    }
}

/* High contrast mode adjustments */
@media (prefers-contrast: high) {
    *::-webkit-scrollbar-thumb {
        background: var(--re-brand-400);
        border: 2px solid var(--re-brand-600);
    }
    
    *::-webkit-scrollbar-track {
        background: #000;
 }
}

/* Reduced motion for accessibility */
@media (prefers-reduced-motion: reduce) {
    *::-webkit-scrollbar-thumb {
        transition: none;
    }
    
    html {
        scroll-behavior: auto;
    }
}

/* Print styles - hide scrollbars */
@media print {
    *::-webkit-scrollbar {
      display: none;
    }
}


/* ===== assets/css/mobile-fixes.css ===== */
/**
 * Mobile Layout Fixes
 * Addresses: hamburger menu issues, text alignment, padding, and overall mobile responsiveness
 */

/* ===================================
   MOBILE VIEWPORT & BASE FIXES
   =================================== */

/* Ensure proper mobile viewport */
@media (max-width: 767px) {
    html {
     -webkit-text-size-adjust: 100%;
        -ms-text-size-adjust: 100%;
    }
    
    body {
        overflow-x: hidden;
   width: 100%;
    }
    
    /* Add padding to all main containers to prevent edge-to-edge content */
    .container {
    padding-left: 15px !important;
   padding-right: 15px !important;
    }
}

/* ===================================
   NAVIGATION BAR MOBILE FIXES
   =================================== */

@media (max-width: 767px) {
    /* Fix navbar container padding */
    nav.navbar .container {
        padding-left: 15px !important;
        padding-right: 15px !important;
    }
    
    /* Fix site title alignment and sizing */
    .navbar-brand {
        font-size: 14px !important;
        padding: 0.5rem 0 !important;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
   max-width: calc(100vw - 100px) !important; /* Leave room for hamburger */
    }
    
    .navbar-brand span {
        font-size: 14px !important;
    }
    
    /* Fix hamburger menu button */
    .navbar-toggler {
      position: relative;
        padding: 0.5rem 0.75rem !important;
    border: 1px solid rgba(255, 255, 255, 0.3) !important;
        border-radius: var(--re-radius-xs);
     background-color: rgba(130, 107, 125, 0.3) !important;
   opacity: 1 !important;
        z-index: 1030;
        margin-left: auto;
    }
    
    /* Fix hamburger icon appearance */
    .navbar-toggler-icon {
        width: 1.5em !important;
      height: 1.5em !important;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.9%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
        opacity: 1 !important;
        display: inline-block;
    }
    
    /* Ensure hamburger is visible and positioned correctly */
    .navbar-toggler:focus,
    .navbar-toggler:hover {
        background-color: rgba(130, 107, 125, 0.5) !important;
        border-color: rgba(255, 255, 255, 0.5) !important;
        opacity: 1 !important;
    }
    
    /* Remove focus outline that might interfere with clicking */
  .navbar-toggler:focus {
      outline: none;
        box-shadow: 0 0 0 0.2rem rgba(130, 107, 125, 0.5);
    }
    
    /* Ensure collapsed state is properly handled */
    .navbar-toggler[aria-expanded="true"] {
        background-color: rgba(130, 107, 125, 0.6) !important;
    }
    
    /* Fix collapsed menu */
    .navbar-collapse {
        /*
         * The dropped menu is glass, like every other panel on the site.
         *
         * It was a near-solid black that predates the token system, so on a
         * phone the navigation opened as a black slab over a palette-tinted
         * page - the one surface that never got the memo. --re-surface is the
         * same token the navbar, the header strip and the cards use, so it
         * follows the palette with them.
         */
        background-color: rgba(0, 0, 0, 0.95) !important;
        background-color: color-mix(in srgb, var(--re-brand-900) 92%, transparent) !important;
        backdrop-filter: var(--re-glass);
        -webkit-backdrop-filter: var(--re-glass);
        border: 1px solid var(--re-border);
        border-radius: var(--re-radius-md);
      margin-top: 0.5rem;
        padding: 1rem;
        max-height: 70vh;
        overflow-y: auto;
        transition: height 0.35s ease;
    }
    
    /* Ensure collapse is working properly */
    .navbar-collapse.collapse:not(.show) {
        display: none;
    }
    
    .navbar-collapse.collapsing {
        height: 0;
        overflow: hidden;
   transition: height 0.35s ease;
    }
    
  /* Fix menu items in mobile view */
    .navbar-nav {
        width: 100%;
    }
    
    .navbar-nav .nav-item {
 padding: 0.5rem 0;
        border-bottom: 1px solid rgba(130, 107, 125, 0.3);
    }
    
    .navbar-nav .nav-item:last-child {
        border-bottom: none;
    }
    
    .navbar-nav .nav-link {
        /* The 14px that was here is gone: these links are Silkscreen now and
           held at 8px site-wide by silkscreen-pixel-grid.css, so the override
           had no effect.

           8px type leaves a 30px row, which is under the 44px a thumb needs -
           the same floor the header controls strip uses. The label stays 8px;
           this is the box around it. */
        padding: 0.5rem 0.6rem !important;
        min-height: 44px;
        display: flex !important;
        align-items: center;
    }
    
    /* Fix dropdown menus in mobile */
    .navbar-nav .dropdown-menu {
        background-color: rgba(0, 0, 0, 0.8) !important;
        border: 1px solid rgba(130, 107, 125, 0.3);
        margin-left: 1rem;
        position: static !important;
        transform: none !important;
    }
    
    .navbar-nav .dropdown-item {
        padding: 0.75rem 1rem !important;
font-size: 12px !important;
    }
}

/* ===================================
   THEME TOGGLE & USER INFO MOBILE
   =================================== */

@media (max-width: 767px) {
  /* Fix theme toggle and user info container */
    .container[style*="padding-top: 74px"] {
     padding-left: 15px !important;
        padding-right: 15px !important;
    padding-top: 74px !important;
    }
    
    /* Stack theme toggle and user info vertically on small screens */
    .container[style*="padding-top: 74px"] > span {
        display: block !important;
        width: 100% !important;
  text-align: center !important;
        margin-bottom: 0.5rem;
 }
    
    .container[style*="padding-top: 74px"] > span.float-end {
        float: none !important;
        text-align: center !important;
    }
    
    /* Fix font size for mobile */
    .container[style*="padding-top: 74px"] span {
        font-size: 10px !important;
    }
}

/* ===================================
   MAIN CONTENT CONTAINER FIXES
   =================================== */

@media (max-width: 767px) {
    /* Add padding to main content container */
    .container:not(nav .container) {
      padding-left: 15px !important;
        padding-right: 15px !important;
        margin-bottom: 1rem;
    }
    
    /* Fix any full-width elements */
    .row {
        margin-left: 0 !important;
        margin-right: 0 !important;
    }
    
    .col,
    [class*="col-"] {
        padding-left: 15px !important;
        padding-right: 15px !important;
    }
}

/* ===================================
   BREADCRUMB MOBILE FIXES
 =================================== */

@media (max-width: 767px) {
    .breadcrumb {
      padding: 0.5rem 1rem !important;
     font-size: 10px !important;
    margin-left: 0 !important;
        margin-right: 0 !important;
    }
    
    .breadcrumb-item {
        font-size: 10px !important;
    }
}

/* ===================================
   CARDS & PANELS MOBILE FIXES
   =================================== */

@media (max-width: 767px) {
    /* Fix card spacing */
    .card {
    margin-bottom: 1rem !important;
    }
    
    /* Fix glassmorphism panels */
  div[style*="background: rgba"] {
   padding: 1rem !important;
    margin-left: 0 !important;
        margin-right: 0 !important;
    }
    
    /* Fix admin dashboard panels */
    .row.mb-4,
    .row.g-3,
    .row.g-2 {
margin-left: 0 !important;
        margin-right: 0 !important;
    }
}

/* ===================================
   BUTTONS & FORMS MOBILE FIXES
   =================================== */

@media (max-width: 767px) {
    /* Make buttons full width on mobile for better touch targets */
    .btn {
   min-height: 44px; /* iOS touch target size */
        padding: 0.75rem 1rem !important;
    }
  
    /* Fix button groups */
    .btn-group {
      display: flex;
        flex-direction: column;
    }
 
    .btn-group .btn {
    margin-bottom: 0.5rem;
    }
    
    /* Fix form inputs for mobile */
    input,
select,
    textarea {
        font-size: 16px !important; /* Prevents iOS zoom on focus */
    }
}

/* ===================================
   TABLE MOBILE FIXES
   =================================== */

@media (max-width: 767px) {
    /* Make tables scrollable horizontally */
    .table-responsive {
 width: 100%;
 overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    }
    
    table {
     font-size: 12px !important;
    }
    
    table th,
    table td {
        padding: 0.5rem !important;
        white-space: nowrap;
    }
}

/* ===================================
   FOOTER MOBILE FIXES
   =================================== */

@media (max-width: 767px) {
    /*
     * Excludes .site-footer, which does its own narrow-screen layout in
     * site-chrome.css. These two declarations were written for the old
     * one-line copyright bar, where centring it was right. The rebuilt
     * footer has a portrait beside a name and two columns of links under
     * it, and centring that reads as a mistake; the padding also doubles
     * up with .footer-inner's own.
     */
    footer:not(.site-footer) {
        padding: 1.5rem 15px !important;
        text-align: center;
    }

    footer:not(.site-footer) .container {
        padding-left: 15px !important;
        padding-right: 15px !important;
    }
}

/* ===================================
   MUSIC PLAYER MOBILE FIXES
   =================================== */

@media (max-width: 767px) {
    #music-player {
        width: calc(100% - 30px) !important;
        margin: 0 15px !important;
        left: 0 !important;
        right: 0 !important;
    }
    
 .music-player-controls {
  flex-wrap: wrap;
        justify-content: center;
    }
  
    .music-player-controls button {
        min-width: 44px;
        min-height: 44px;
    }
}

/* ===================================
   GALLERY & IMAGES MOBILE FIXES
   =================================== */

@media (max-width: 767px) {
    /* Fix gallery images */
    .gallery-item,
    .portfolio-item {
        margin-bottom: 1rem;
    }
    
    img {
  max-width: 100%;
        height: auto;
    }
    
    /* Fix lightbox on mobile */
    .lightbox {
        padding: 15px !important;
    }
}

/* ===================================
   ADMIN DASHBOARD MOBILE FIXES
   =================================== */

@media (max-width: 767px) {
    /* Stack dashboard stats vertically */
    .admin-dashboard .row > div[class*="col-"] {
        margin-bottom: 1rem;
    }
    
    /* Fix metric cards */
    div[style*="text-align: center"] {
        padding: 1rem !important;
    }
 
    /* Fix tabs on mobile */
    .nav-tabs {
     flex-wrap: nowrap;
 overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
    
    .nav-tabs .nav-link {
        white-space: nowrap;
        font-size: 10px !important;
      padding: 0.5rem 0.75rem !important;
    }
}

/* ===================================
   TOAST NOTIFICATIONS MOBILE FIXES
   =================================== */

@media (max-width: 767px) {
    .toast-container {
      bottom: 70px !important; /* Above music player */
        left: 15px !important;
        right: 15px !important;
        width: auto !important;
    }
 
    .toast {
        max-width: 100% !important;
        font-size: 12px !important;
    }
}

/* ===================================
   UTILITY CLASSES FOR MOBILE
   =================================== */

@media (max-width: 767px) {
    .mobile-hide {
      display: none !important;
    }
  
    .mobile-show {
        display: block !important;
    }
    
    .mobile-full-width {
        width: 100% !important;
    }
    
    .mobile-text-center {
  text-align: center !important;
    }
    
    .mobile-no-padding {
padding: 0 !important;
    }
    
  .mobile-padding {
        padding: 15px !important;
    }
}

/* ===================================
   LANDSCAPE MODE ADJUSTMENTS
   =================================== */

@media (max-width: 767px) and (orientation: landscape) {
    /* Reduce navbar height in landscape */
    nav.navbar {
   padding-top: 0.5rem !important;
        padding-bottom: 0.5rem !important;
    }
    
    .navbar-brand {
        font-size: 12px !important;
    }
    
    /* Reduce top padding in landscape */
    .container[style*="padding-top: 74px"] {
        padding-top: 60px !important;
    }
}

/* ===================================
   VERY SMALL SCREENS (< 375px)
   =================================== */

@media (max-width: 374px) {
    .navbar-brand {
 font-size: 12px !important;
        max-width: calc(100vw - 80px) !important;
    }
    
    .container {
        padding-left: 10px !important;
        padding-right: 10px !important;
  }
    
.btn {
    font-size: 12px !important;
        padding: 0.5rem 0.75rem !important;
  }
}


/* ===== assets/css/prevent-effect-clipping.css ===== */
/**
 * Prevent Effect Clipping
 * Ensures all glassmorphism, shadows, transforms, and effects are never cut off by containers
 */

/* ============================================
   GLOBAL OVERFLOW FIXES
   ============================================ */

/* Allow body to show overflows for effects */
body {
  overflow-x: hidden; /* Keep horizontal scroll prevention */
  overflow-y: auto; /* Allow vertical scroll */
}

/* Main container should not clip effects */
.container,
.container-fluid {
  overflow: visible !important;
}

/* ============================================
   ABOUT PAGE SECTIONS
   ============================================ */

/* Hero section - allow shadow and glass effects to overflow */
.about-hero,
.about-hero-container {
  overflow: visible !important;
}

/* About sections - allow transform/shadows to overflow */
.about-section,
.about-section.contained,
.about-section.open {
  overflow: visible !important;
}

/* Section content divs */
.about-sections {
  overflow: visible !important;
}

/* Skills grid and categories */
.skills-grid,
.skill-category {
  overflow: visible !important;
}

/* Experience grid and items */
.experience-grid,
.experience-item {
  overflow: visible !important;
}

/* ============================================
   GAMING CAROUSEL SECTIONS
   ============================================ */

/* Gaming section containers */
.gaming-section,
.gaming-section.currently-playing,
.gaming-section.favorite-games {
  overflow: visible !important;
}

/* Section headers */
.section-header-accent {
  overflow: visible !important;
}

/* Carousel container - special case */
.games-carousel-container {
  overflow: visible !important; /* Allow shadows to extend */
  position: relative;
}

/* Carousel track needs horizontal scroll */
.games-carousel {
  overflow-x: auto !important;
  overflow-y: visible !important; /* Allow vertical effects */
  padding-bottom: 20px; /* Space for shadows below cards */
  padding-top: 10px; /* Space for shadows above cards */
}

/* Game cards - allow all effects to overflow */
.game-card {
  overflow: visible !important;
}

.game-card .media {
  overflow: hidden !important; /* Keep images contained */
  border-radius: var(--re-radius-lg) var(--re-radius-lg) 0 0;
}

.game-card .content {
  overflow: visible !important;
}

/* ============================================
   GLASSMORPHISM CONTAINERS
   ============================================ */

/* All glassmorphism containers should not clip shadows */
div[style*="backdrop-filter"],
div[style*="-webkit-backdrop-filter"],
div[style*="background: rgba"] {
  overflow: visible !important;
}

/* Specific glass containers */
[class*="glass-"],
[class*="-glass"] {
  overflow: visible !important;
}

/* ============================================
   CARDS AND PANELS
   ============================================ */

/* Project cards */
.project-card {
  overflow: visible !important;
}

.project-card .media,
.project-card img {
  overflow: hidden !important; /* Keep images contained */
}

/* Article cards */
.article-card {
  overflow: visible !important;
}

/* Admin dashboard cards */
.admin-card,
.admin-section,
.stat-card {
  overflow: visible !important;
}

/* ============================================
   BUTTONS WITH HOVER EFFECTS
   ============================================ */

/* Buttons need space for transform effects */
.btn,
button,
.control-btn,
.quick-action-btn {
  overflow: visible !important;
}

/* Button groups */
.btn-group,
.player-controls {
  overflow: visible !important;
}

/* ============================================
   NAVIGATION
   ============================================ */

/* Navbar */
nav.navbar {
  overflow: visible !important;
}

.navbar-collapse {
  overflow: visible !important;
}

/* Dropdown menus need to overflow */
.dropdown-menu {
  overflow: visible !important;
}

/* ============================================
   BREADCRUMB
   ============================================ */

.breadcrumb {
  overflow: visible !important;
}

.breadcrumb-item {
  overflow: visible !important;
}

/* ============================================
   MUSIC PLAYER
   ============================================ */

.persistent-player {
  overflow: visible !important;
}

.player-container {
  overflow: visible !important;
}

/* Playlist needs vertical scroll */
.playlist-container {
  overflow-y: auto !important;
  overflow-x: visible !important;
}

/* ============================================
   IMAGES WITH EFFECTS
   ============================================ */

/* Profile images */
.profile-image-wrapper {
  overflow: visible !important;
}

.profile-image {
  overflow: hidden !important; /* Keep image cropped */
}

/* ============================================
   ROW AND COLUMN LAYOUT
   ============================================ */

.row {
  overflow: visible !important;
}

[class*="col-"] {
  overflow: visible !important;
}

/* ============================================
   SPECIFIC FIX FOR HOVER TRANSFORMS
   ============================================ */

/* Elements with translateY hover effects */
[onmouseover*="translateY"],
[class*=":hover"] {
  overflow: visible !important;
}

/* Parent containers of hoverable items */
.about-section.contained,
.game-card,
.project-card,
.experience-item,
.skill-category {
  overflow: visible !important;
}

/* ============================================
   HEADER CONTROLS - DROPDOWN MUST ESCAPE
   ============================================ */

/* Header controls panel - allow dropdown to overflow */
.header-controls-panel {
  overflow: visible !important;
}

/* Container wrapping header controls */
.header-controls-panel > * {
  overflow: visible !important;
}

/* Language dropdown wrapper */
.header-controls-lang,
span.dropdown.header-controls-lang {
  overflow: visible !important;
}

/* User section */
.header-controls-user {
  overflow: visible !important;
}

/* Parent containers of header controls */
.container:has(.header-controls-panel) {
  overflow: visible !important;
}

/* ============================================
   EXCEPTIONS - KEEP OVERFLOW HIDDEN
   ============================================ */

/* Images should stay clipped */
img {
  overflow: hidden !important;
}

/* Video embeds */
video,
iframe {
  overflow: hidden !important;
}

/* Code blocks */
pre,
code {
  overflow-x: auto !important;
}

/* Tables with horizontal scroll */
.table-responsive {
  overflow-x: auto !important;
  overflow-y: visible !important;
}

/* ============================================
   CANVAS ELEMENTS
   ============================================ */

/* Particle network canvas */
#particle-network-container,
#demo-canvas,
.large-header {
  overflow: hidden !important; /* Canvas needs to be contained */
}

/* ============================================
   MOBILE SPECIFIC
   ============================================ */

@media (max-width: 768px) {
  /* Mobile still needs some overflow management */
  body {
    overflow-x: hidden !important;
  }
  
  /* But effects should still be visible */
  .container {
    overflow: visible !important;
  }
  
  /* Gaming carousel on mobile */
  .games-carousel {
    overflow-x: auto !important;
    overflow-y: visible !important;
    -webkit-overflow-scrolling: touch;
  }
}

/* ============================================
   Z-INDEX STACKING FOR EFFECTS
   ============================================ */

/* Ensure effects don't get covered */
.about-section.contained:hover,
.game-card:hover,
.experience-item:hover,
.skill-category:hover {
  z-index: 2 !important;
  position: relative !important;
}

/* ============================================
   PADDING ADJUSTMENTS FOR EFFECTS
   ============================================ */

/* Add padding to parent containers to accommodate overflow effects */
.about-sections {
  padding: 20px 0; /* Vertical space for shadows */
}

.skills-grid,
.experience-grid {
  padding: 10px 0; /* Space for hover effects */
}

/* ============================================
   PRINT STYLES
   ============================================ */

@media print {
  /* Reset overflows for printing */
  * {
    overflow: visible !important;
  }
}


/* ===== assets/css/styles.css ===== */
body {
  /* Background gradient now handled by background-gradient-filmgrain-fix.css */
  background-color: #111111; /* Fallback only */
  margin: 0;
  overflow-x: hidden;
}

.large-header {
position: relative;
  width: 100%;
  background: #333;
  overflow: hidden;
  background-size: cover;
  background-position: center center;
  z-index: 1;
}

.bg {
  position: fixed;
  top: -50%;
  left: -50%;
  right: -50%;
  bottom: -50%;
  width: 200%;
  height: 200vh;
  background: transparent url('https://realityengine.net/assets/img/noise-transparent.png') repeat 0 0;
  background-repeat: repeat;
  animation: bg-animation .2s infinite !important;
  opacity: .9;
  visibility: visible;
  z-index: 0;
  pointer-events: none;
}

@keyframes bg-animation {
  0% { transform: translate(0, 0); }
  10% { transform: translate(-5%, -5%); }
  20% { transform: translate(-10%, 5%); }
  30% { transform: translate(5%, -10%); }
  40% { transform: translate(-5%, 15%); }
  50% { transform: translate(-10%, 5%); }
  60% { transform: translate(15%, 0); }
  70% { transform: translate(0, 10%); }
  80% { transform: translate(-15%, 0); }
  90% { transform: translate(10%, 5%); }
  100% { transform: translate(5%, 0); }
}



/* ===== assets/css/aos.min.css ===== */
/* AOS version 2.1.1 */

[data-aos][data-aos][data-aos-duration='50'], body[data-aos-duration='50'] [data-aos] {
  transition-duration: 50ms;
}

[data-aos][data-aos][data-aos-delay='50'], body[data-aos-delay='50'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='50'].aos-animate, body[data-aos-delay='50'] [data-aos].aos-animate {
  transition-delay: 50ms;
}

[data-aos][data-aos][data-aos-duration='100'], body[data-aos-duration='100'] [data-aos] {
  transition-duration: .1s;
}

[data-aos][data-aos][data-aos-delay='100'], body[data-aos-delay='100'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='100'].aos-animate, body[data-aos-delay='100'] [data-aos].aos-animate {
  transition-delay: .1s;
}

[data-aos][data-aos][data-aos-duration='150'], body[data-aos-duration='150'] [data-aos] {
  transition-duration: .15s;
}

[data-aos][data-aos][data-aos-delay='150'], body[data-aos-delay='150'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='150'].aos-animate, body[data-aos-delay='150'] [data-aos].aos-animate {
  transition-delay: .15s;
}

[data-aos][data-aos][data-aos-duration='200'], body[data-aos-duration='200'] [data-aos] {
  transition-duration: .2s;
}

[data-aos][data-aos][data-aos-delay='200'], body[data-aos-delay='200'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='200'].aos-animate, body[data-aos-delay='200'] [data-aos].aos-animate {
  transition-delay: .2s;
}

[data-aos][data-aos][data-aos-duration='250'], body[data-aos-duration='250'] [data-aos] {
  transition-duration: .25s;
}

[data-aos][data-aos][data-aos-delay='250'], body[data-aos-delay='250'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='250'].aos-animate, body[data-aos-delay='250'] [data-aos].aos-animate {
  transition-delay: .25s;
}

[data-aos][data-aos][data-aos-duration='300'], body[data-aos-duration='300'] [data-aos] {
  transition-duration: .3s;
}

[data-aos][data-aos][data-aos-delay='300'], body[data-aos-delay='300'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='300'].aos-animate, body[data-aos-delay='300'] [data-aos].aos-animate {
  transition-delay: .3s;
}

[data-aos][data-aos][data-aos-duration='350'], body[data-aos-duration='350'] [data-aos] {
  transition-duration: .35s;
}

[data-aos][data-aos][data-aos-delay='350'], body[data-aos-delay='350'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='350'].aos-animate, body[data-aos-delay='350'] [data-aos].aos-animate {
  transition-delay: .35s;
}

[data-aos][data-aos][data-aos-duration='400'], body[data-aos-duration='400'] [data-aos] {
  transition-duration: .4s;
}

[data-aos][data-aos][data-aos-delay='400'], body[data-aos-delay='400'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='400'].aos-animate, body[data-aos-delay='400'] [data-aos].aos-animate {
  transition-delay: .4s;
}

[data-aos][data-aos][data-aos-duration='450'], body[data-aos-duration='450'] [data-aos] {
  transition-duration: .45s;
}

[data-aos][data-aos][data-aos-delay='450'], body[data-aos-delay='450'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='450'].aos-animate, body[data-aos-delay='450'] [data-aos].aos-animate {
  transition-delay: .45s;
}

[data-aos][data-aos][data-aos-duration='500'], body[data-aos-duration='500'] [data-aos] {
  transition-duration: .5s;
}

[data-aos][data-aos][data-aos-delay='500'], body[data-aos-delay='500'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='500'].aos-animate, body[data-aos-delay='500'] [data-aos].aos-animate {
  transition-delay: .5s;
}

[data-aos][data-aos][data-aos-duration='550'], body[data-aos-duration='550'] [data-aos] {
  transition-duration: .55s;
}

[data-aos][data-aos][data-aos-delay='550'], body[data-aos-delay='550'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='550'].aos-animate, body[data-aos-delay='550'] [data-aos].aos-animate {
  transition-delay: .55s;
}

[data-aos][data-aos][data-aos-duration='600'], body[data-aos-duration='600'] [data-aos] {
  transition-duration: .6s;
}

[data-aos][data-aos][data-aos-delay='600'], body[data-aos-delay='600'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='600'].aos-animate, body[data-aos-delay='600'] [data-aos].aos-animate {
  transition-delay: .6s;
}

[data-aos][data-aos][data-aos-duration='650'], body[data-aos-duration='650'] [data-aos] {
  transition-duration: .65s;
}

[data-aos][data-aos][data-aos-delay='650'], body[data-aos-delay='650'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='650'].aos-animate, body[data-aos-delay='650'] [data-aos].aos-animate {
  transition-delay: .65s;
}

[data-aos][data-aos][data-aos-duration='700'], body[data-aos-duration='700'] [data-aos] {
  transition-duration: .7s;
}

[data-aos][data-aos][data-aos-delay='700'], body[data-aos-delay='700'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='700'].aos-animate, body[data-aos-delay='700'] [data-aos].aos-animate {
  transition-delay: .7s;
}

[data-aos][data-aos][data-aos-duration='750'], body[data-aos-duration='750'] [data-aos] {
  transition-duration: .75s;
}

[data-aos][data-aos][data-aos-delay='750'], body[data-aos-delay='750'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='750'].aos-animate, body[data-aos-delay='750'] [data-aos].aos-animate {
  transition-delay: .75s;
}

[data-aos][data-aos][data-aos-duration='800'], body[data-aos-duration='800'] [data-aos] {
  transition-duration: .8s;
}

[data-aos][data-aos][data-aos-delay='800'], body[data-aos-delay='800'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='800'].aos-animate, body[data-aos-delay='800'] [data-aos].aos-animate {
  transition-delay: .8s;
}

[data-aos][data-aos][data-aos-duration='850'], body[data-aos-duration='850'] [data-aos] {
  transition-duration: .85s;
}

[data-aos][data-aos][data-aos-delay='850'], body[data-aos-delay='850'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='850'].aos-animate, body[data-aos-delay='850'] [data-aos].aos-animate {
  transition-delay: .85s;
}

[data-aos][data-aos][data-aos-duration='900'], body[data-aos-duration='900'] [data-aos] {
  transition-duration: .9s;
}

[data-aos][data-aos][data-aos-delay='900'], body[data-aos-delay='900'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='900'].aos-animate, body[data-aos-delay='900'] [data-aos].aos-animate {
  transition-delay: .9s;
}

[data-aos][data-aos][data-aos-duration='950'], body[data-aos-duration='950'] [data-aos] {
  transition-duration: .95s;
}

[data-aos][data-aos][data-aos-delay='950'], body[data-aos-delay='950'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='950'].aos-animate, body[data-aos-delay='950'] [data-aos].aos-animate {
  transition-delay: .95s;
}

[data-aos][data-aos][data-aos-duration='1000'], body[data-aos-duration='1000'] [data-aos] {
  transition-duration: 1s;
}

[data-aos][data-aos][data-aos-delay='1000'], body[data-aos-delay='1000'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1000'].aos-animate, body[data-aos-delay='1000'] [data-aos].aos-animate {
  transition-delay: 1s;
}

[data-aos][data-aos][data-aos-duration='1050'], body[data-aos-duration='1050'] [data-aos] {
  transition-duration: 1.05s;
}

[data-aos][data-aos][data-aos-delay='1050'], body[data-aos-delay='1050'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1050'].aos-animate, body[data-aos-delay='1050'] [data-aos].aos-animate {
  transition-delay: 1.05s;
}

[data-aos][data-aos][data-aos-duration='1100'], body[data-aos-duration='1100'] [data-aos] {
  transition-duration: 1.1s;
}

[data-aos][data-aos][data-aos-delay='1100'], body[data-aos-delay='1100'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1100'].aos-animate, body[data-aos-delay='1100'] [data-aos].aos-animate {
  transition-delay: 1.1s;
}

[data-aos][data-aos][data-aos-duration='1150'], body[data-aos-duration='1150'] [data-aos] {
  transition-duration: 1.15s;
}

[data-aos][data-aos][data-aos-delay='1150'], body[data-aos-delay='1150'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1150'].aos-animate, body[data-aos-delay='1150'] [data-aos].aos-animate {
  transition-delay: 1.15s;
}

[data-aos][data-aos][data-aos-duration='1200'], body[data-aos-duration='1200'] [data-aos] {
  transition-duration: 1.2s;
}

[data-aos][data-aos][data-aos-delay='1200'], body[data-aos-delay='1200'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1200'].aos-animate, body[data-aos-delay='1200'] [data-aos].aos-animate {
  transition-delay: 1.2s;
}

[data-aos][data-aos][data-aos-duration='1250'], body[data-aos-duration='1250'] [data-aos] {
  transition-duration: 1.25s;
}

[data-aos][data-aos][data-aos-delay='1250'], body[data-aos-delay='1250'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1250'].aos-animate, body[data-aos-delay='1250'] [data-aos].aos-animate {
  transition-delay: 1.25s;
}

[data-aos][data-aos][data-aos-duration='1300'], body[data-aos-duration='1300'] [data-aos] {
  transition-duration: 1.3s;
}

[data-aos][data-aos][data-aos-delay='1300'], body[data-aos-delay='1300'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1300'].aos-animate, body[data-aos-delay='1300'] [data-aos].aos-animate {
  transition-delay: 1.3s;
}

[data-aos][data-aos][data-aos-duration='1350'], body[data-aos-duration='1350'] [data-aos] {
  transition-duration: 1.35s;
}

[data-aos][data-aos][data-aos-delay='1350'], body[data-aos-delay='1350'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1350'].aos-animate, body[data-aos-delay='1350'] [data-aos].aos-animate {
  transition-delay: 1.35s;
}

[data-aos][data-aos][data-aos-duration='1400'], body[data-aos-duration='1400'] [data-aos] {
  transition-duration: 1.4s;
}

[data-aos][data-aos][data-aos-delay='1400'], body[data-aos-delay='1400'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1400'].aos-animate, body[data-aos-delay='1400'] [data-aos].aos-animate {
  transition-delay: 1.4s;
}

[data-aos][data-aos][data-aos-duration='1450'], body[data-aos-duration='1450'] [data-aos] {
  transition-duration: 1.45s;
}

[data-aos][data-aos][data-aos-delay='1450'], body[data-aos-delay='1450'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1450'].aos-animate, body[data-aos-delay='1450'] [data-aos].aos-animate {
  transition-delay: 1.45s;
}

[data-aos][data-aos][data-aos-duration='1500'], body[data-aos-duration='1500'] [data-aos] {
  transition-duration: 1.5s;
}

[data-aos][data-aos][data-aos-delay='1500'], body[data-aos-delay='1500'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1500'].aos-animate, body[data-aos-delay='1500'] [data-aos].aos-animate {
  transition-delay: 1.5s;
}

[data-aos][data-aos][data-aos-duration='1550'], body[data-aos-duration='1550'] [data-aos] {
  transition-duration: 1.55s;
}

[data-aos][data-aos][data-aos-delay='1550'], body[data-aos-delay='1550'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1550'].aos-animate, body[data-aos-delay='1550'] [data-aos].aos-animate {
  transition-delay: 1.55s;
}

[data-aos][data-aos][data-aos-duration='1600'], body[data-aos-duration='1600'] [data-aos] {
  transition-duration: 1.6s;
}

[data-aos][data-aos][data-aos-delay='1600'], body[data-aos-delay='1600'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1600'].aos-animate, body[data-aos-delay='1600'] [data-aos].aos-animate {
  transition-delay: 1.6s;
}

[data-aos][data-aos][data-aos-duration='1650'], body[data-aos-duration='1650'] [data-aos] {
  transition-duration: 1.65s;
}

[data-aos][data-aos][data-aos-delay='1650'], body[data-aos-delay='1650'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1650'].aos-animate, body[data-aos-delay='1650'] [data-aos].aos-animate {
  transition-delay: 1.65s;
}

[data-aos][data-aos][data-aos-duration='1700'], body[data-aos-duration='1700'] [data-aos] {
  transition-duration: 1.7s;
}

[data-aos][data-aos][data-aos-delay='1700'], body[data-aos-delay='1700'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1700'].aos-animate, body[data-aos-delay='1700'] [data-aos].aos-animate {
  transition-delay: 1.7s;
}

[data-aos][data-aos][data-aos-duration='1750'], body[data-aos-duration='1750'] [data-aos] {
  transition-duration: 1.75s;
}

[data-aos][data-aos][data-aos-delay='1750'], body[data-aos-delay='1750'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1750'].aos-animate, body[data-aos-delay='1750'] [data-aos].aos-animate {
  transition-delay: 1.75s;
}

[data-aos][data-aos][data-aos-duration='1800'], body[data-aos-duration='1800'] [data-aos] {
  transition-duration: 1.8s;
}

[data-aos][data-aos][data-aos-delay='1800'], body[data-aos-delay='1800'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1800'].aos-animate, body[data-aos-delay='1800'] [data-aos].aos-animate {
  transition-delay: 1.8s;
}

[data-aos][data-aos][data-aos-duration='1850'], body[data-aos-duration='1850'] [data-aos] {
  transition-duration: 1.85s;
}

[data-aos][data-aos][data-aos-delay='1850'], body[data-aos-delay='1850'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1850'].aos-animate, body[data-aos-delay='1850'] [data-aos].aos-animate {
  transition-delay: 1.85s;
}

[data-aos][data-aos][data-aos-duration='1900'], body[data-aos-duration='1900'] [data-aos] {
  transition-duration: 1.9s;
}

[data-aos][data-aos][data-aos-delay='1900'], body[data-aos-delay='1900'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1900'].aos-animate, body[data-aos-delay='1900'] [data-aos].aos-animate {
  transition-delay: 1.9s;
}

[data-aos][data-aos][data-aos-duration='1950'], body[data-aos-duration='1950'] [data-aos] {
  transition-duration: 1.95s;
}

[data-aos][data-aos][data-aos-delay='1950'], body[data-aos-delay='1950'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='1950'].aos-animate, body[data-aos-delay='1950'] [data-aos].aos-animate {
  transition-delay: 1.95s;
}

[data-aos][data-aos][data-aos-duration='2000'], body[data-aos-duration='2000'] [data-aos] {
  transition-duration: 2s;
}

[data-aos][data-aos][data-aos-delay='2000'], body[data-aos-delay='2000'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2000'].aos-animate, body[data-aos-delay='2000'] [data-aos].aos-animate {
  transition-delay: 2s;
}

[data-aos][data-aos][data-aos-duration='2050'], body[data-aos-duration='2050'] [data-aos] {
  transition-duration: 2.05s;
}

[data-aos][data-aos][data-aos-delay='2050'], body[data-aos-delay='2050'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2050'].aos-animate, body[data-aos-delay='2050'] [data-aos].aos-animate {
  transition-delay: 2.05s;
}

[data-aos][data-aos][data-aos-duration='2100'], body[data-aos-duration='2100'] [data-aos] {
  transition-duration: 2.1s;
}

[data-aos][data-aos][data-aos-delay='2100'], body[data-aos-delay='2100'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2100'].aos-animate, body[data-aos-delay='2100'] [data-aos].aos-animate {
  transition-delay: 2.1s;
}

[data-aos][data-aos][data-aos-duration='2150'], body[data-aos-duration='2150'] [data-aos] {
  transition-duration: 2.15s;
}

[data-aos][data-aos][data-aos-delay='2150'], body[data-aos-delay='2150'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2150'].aos-animate, body[data-aos-delay='2150'] [data-aos].aos-animate {
  transition-delay: 2.15s;
}

[data-aos][data-aos][data-aos-duration='2200'], body[data-aos-duration='2200'] [data-aos] {
  transition-duration: 2.2s;
}

[data-aos][data-aos][data-aos-delay='2200'], body[data-aos-delay='2200'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2200'].aos-animate, body[data-aos-delay='2200'] [data-aos].aos-animate {
  transition-delay: 2.2s;
}

[data-aos][data-aos][data-aos-duration='2250'], body[data-aos-duration='2250'] [data-aos] {
  transition-duration: 2.25s;
}

[data-aos][data-aos][data-aos-delay='2250'], body[data-aos-delay='2250'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2250'].aos-animate, body[data-aos-delay='2250'] [data-aos].aos-animate {
  transition-delay: 2.25s;
}

[data-aos][data-aos][data-aos-duration='2300'], body[data-aos-duration='2300'] [data-aos] {
  transition-duration: 2.3s;
}

[data-aos][data-aos][data-aos-delay='2300'], body[data-aos-delay='2300'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2300'].aos-animate, body[data-aos-delay='2300'] [data-aos].aos-animate {
  transition-delay: 2.3s;
}

[data-aos][data-aos][data-aos-duration='2350'], body[data-aos-duration='2350'] [data-aos] {
  transition-duration: 2.35s;
}

[data-aos][data-aos][data-aos-delay='2350'], body[data-aos-delay='2350'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2350'].aos-animate, body[data-aos-delay='2350'] [data-aos].aos-animate {
  transition-delay: 2.35s;
}

[data-aos][data-aos][data-aos-duration='2400'], body[data-aos-duration='2400'] [data-aos] {
  transition-duration: 2.4s;
}

[data-aos][data-aos][data-aos-delay='2400'], body[data-aos-delay='2400'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2400'].aos-animate, body[data-aos-delay='2400'] [data-aos].aos-animate {
  transition-delay: 2.4s;
}

[data-aos][data-aos][data-aos-duration='2450'], body[data-aos-duration='2450'] [data-aos] {
  transition-duration: 2.45s;
}

[data-aos][data-aos][data-aos-delay='2450'], body[data-aos-delay='2450'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2450'].aos-animate, body[data-aos-delay='2450'] [data-aos].aos-animate {
  transition-delay: 2.45s;
}

[data-aos][data-aos][data-aos-duration='2500'], body[data-aos-duration='2500'] [data-aos] {
  transition-duration: 2.5s;
}

[data-aos][data-aos][data-aos-delay='2500'], body[data-aos-delay='2500'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2500'].aos-animate, body[data-aos-delay='2500'] [data-aos].aos-animate {
  transition-delay: 2.5s;
}

[data-aos][data-aos][data-aos-duration='2550'], body[data-aos-duration='2550'] [data-aos] {
  transition-duration: 2.55s;
}

[data-aos][data-aos][data-aos-delay='2550'], body[data-aos-delay='2550'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2550'].aos-animate, body[data-aos-delay='2550'] [data-aos].aos-animate {
  transition-delay: 2.55s;
}

[data-aos][data-aos][data-aos-duration='2600'], body[data-aos-duration='2600'] [data-aos] {
  transition-duration: 2.6s;
}

[data-aos][data-aos][data-aos-delay='2600'], body[data-aos-delay='2600'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2600'].aos-animate, body[data-aos-delay='2600'] [data-aos].aos-animate {
  transition-delay: 2.6s;
}

[data-aos][data-aos][data-aos-duration='2650'], body[data-aos-duration='2650'] [data-aos] {
  transition-duration: 2.65s;
}

[data-aos][data-aos][data-aos-delay='2650'], body[data-aos-delay='2650'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2650'].aos-animate, body[data-aos-delay='2650'] [data-aos].aos-animate {
  transition-delay: 2.65s;
}

[data-aos][data-aos][data-aos-duration='2700'], body[data-aos-duration='2700'] [data-aos] {
  transition-duration: 2.7s;
}

[data-aos][data-aos][data-aos-delay='2700'], body[data-aos-delay='2700'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2700'].aos-animate, body[data-aos-delay='2700'] [data-aos].aos-animate {
  transition-delay: 2.7s;
}

[data-aos][data-aos][data-aos-duration='2750'], body[data-aos-duration='2750'] [data-aos] {
  transition-duration: 2.75s;
}

[data-aos][data-aos][data-aos-delay='2750'], body[data-aos-delay='2750'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2750'].aos-animate, body[data-aos-delay='2750'] [data-aos].aos-animate {
  transition-delay: 2.75s;
}

[data-aos][data-aos][data-aos-duration='2800'], body[data-aos-duration='2800'] [data-aos] {
  transition-duration: 2.8s;
}

[data-aos][data-aos][data-aos-delay='2800'], body[data-aos-delay='2800'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2800'].aos-animate, body[data-aos-delay='2800'] [data-aos].aos-animate {
  transition-delay: 2.8s;
}

[data-aos][data-aos][data-aos-duration='2850'], body[data-aos-duration='2850'] [data-aos] {
  transition-duration: 2.85s;
}

[data-aos][data-aos][data-aos-delay='2850'], body[data-aos-delay='2850'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2850'].aos-animate, body[data-aos-delay='2850'] [data-aos].aos-animate {
  transition-delay: 2.85s;
}

[data-aos][data-aos][data-aos-duration='2900'], body[data-aos-duration='2900'] [data-aos] {
  transition-duration: 2.9s;
}

[data-aos][data-aos][data-aos-delay='2900'], body[data-aos-delay='2900'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2900'].aos-animate, body[data-aos-delay='2900'] [data-aos].aos-animate {
  transition-delay: 2.9s;
}

[data-aos][data-aos][data-aos-duration='2950'], body[data-aos-duration='2950'] [data-aos] {
  transition-duration: 2.95s;
}

[data-aos][data-aos][data-aos-delay='2950'], body[data-aos-delay='2950'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='2950'].aos-animate, body[data-aos-delay='2950'] [data-aos].aos-animate {
  transition-delay: 2.95s;
}

[data-aos][data-aos][data-aos-duration='3000'], body[data-aos-duration='3000'] [data-aos] {
  transition-duration: 3s;
}

[data-aos][data-aos][data-aos-delay='3000'], body[data-aos-delay='3000'] [data-aos] {
  transition-delay: 0;
}

[data-aos][data-aos][data-aos-delay='3000'].aos-animate, body[data-aos-delay='3000'] [data-aos].aos-animate {
  transition-delay: 3s;
}

[data-aos][data-aos][data-aos-easing=linear], body[data-aos-easing=linear] [data-aos] {
  transition-timing-function: cubic-bezier(.25,.25,.75,.75);
}

[data-aos][data-aos][data-aos-easing=ease], body[data-aos-easing=ease] [data-aos] {
  transition-timing-function: ease;
}

[data-aos][data-aos][data-aos-easing=ease-in], body[data-aos-easing=ease-in] [data-aos] {
  transition-timing-function: ease-in;
}

[data-aos][data-aos][data-aos-easing=ease-out], body[data-aos-easing=ease-out] [data-aos] {
  transition-timing-function: ease-out;
}

[data-aos][data-aos][data-aos-easing=ease-in-out], body[data-aos-easing=ease-in-out] [data-aos] {
  transition-timing-function: ease-in-out;
}

[data-aos][data-aos][data-aos-easing=ease-in-back], body[data-aos-easing=ease-in-back] [data-aos] {
  transition-timing-function: cubic-bezier(.6,-.28,.735,.045);
}

[data-aos][data-aos][data-aos-easing=ease-out-back], body[data-aos-easing=ease-out-back] [data-aos] {
  transition-timing-function: cubic-bezier(.175,.885,.32,1.275);
}

[data-aos][data-aos][data-aos-easing=ease-in-out-back], body[data-aos-easing=ease-in-out-back] [data-aos] {
  transition-timing-function: cubic-bezier(.68,-.55,.265,1.55);
}

[data-aos][data-aos][data-aos-easing=ease-in-sine], body[data-aos-easing=ease-in-sine] [data-aos] {
  transition-timing-function: cubic-bezier(.47,0,.745,.715);
}

[data-aos][data-aos][data-aos-easing=ease-out-sine], body[data-aos-easing=ease-out-sine] [data-aos] {
  transition-timing-function: cubic-bezier(.39,.575,.565,1);
}

[data-aos][data-aos][data-aos-easing=ease-in-out-sine], body[data-aos-easing=ease-in-out-sine] [data-aos] {
  transition-timing-function: cubic-bezier(.445,.05,.55,.95);
}

[data-aos][data-aos][data-aos-easing=ease-in-quad], body[data-aos-easing=ease-in-quad] [data-aos] {
  transition-timing-function: cubic-bezier(.55,.085,.68,.53);
}

[data-aos][data-aos][data-aos-easing=ease-out-quad], body[data-aos-easing=ease-out-quad] [data-aos] {
  transition-timing-function: cubic-bezier(.25,.46,.45,.94);
}

[data-aos][data-aos][data-aos-easing=ease-in-out-quad], body[data-aos-easing=ease-in-out-quad] [data-aos] {
  transition-timing-function: cubic-bezier(.455,.03,.515,.955);
}

[data-aos][data-aos][data-aos-easing=ease-in-cubic], body[data-aos-easing=ease-in-cubic] [data-aos] {
  transition-timing-function: cubic-bezier(.55,.085,.68,.53);
}

[data-aos][data-aos][data-aos-easing=ease-out-cubic], body[data-aos-easing=ease-out-cubic] [data-aos] {
  transition-timing-function: cubic-bezier(.25,.46,.45,.94);
}

[data-aos][data-aos][data-aos-easing=ease-in-out-cubic], body[data-aos-easing=ease-in-out-cubic] [data-aos] {
  transition-timing-function: cubic-bezier(.455,.03,.515,.955);
}

[data-aos][data-aos][data-aos-easing=ease-in-quart], body[data-aos-easing=ease-in-quart] [data-aos] {
  transition-timing-function: cubic-bezier(.55,.085,.68,.53);
}

[data-aos][data-aos][data-aos-easing=ease-out-quart], body[data-aos-easing=ease-out-quart] [data-aos] {
  transition-timing-function: cubic-bezier(.25,.46,.45,.94);
}

[data-aos][data-aos][data-aos-easing=ease-in-out-quart], body[data-aos-easing=ease-in-out-quart] [data-aos] {
  transition-timing-function: cubic-bezier(.455,.03,.515,.955);
}

[data-aos^=fade][data-aos^=fade] {
  opacity: 0;
  transition-property: opacity,transform;
}

[data-aos^=fade][data-aos^=fade].aos-animate {
  opacity: 1;
  transform: translate(0);
}

[data-aos=fade-up] {
  transform: translateY(100px);
}

[data-aos=fade-down] {
  transform: translateY(-100px);
}

[data-aos=fade-right] {
  transform: translate(-100px);
}

[data-aos=fade-left] {
  transform: translate(100px);
}

[data-aos=fade-up-right] {
  transform: translate(-100px,100px);
}

[data-aos=fade-up-left] {
  transform: translate(100px,100px);
}

[data-aos=fade-down-right] {
  transform: translate(-100px,-100px);
}

[data-aos=fade-down-left] {
  transform: translate(100px,-100px);
}

[data-aos^=zoom][data-aos^=zoom] {
  opacity: 0;
  transition-property: opacity,transform;
}

[data-aos^=zoom][data-aos^=zoom].aos-animate {
  opacity: 1;
  transform: translate(0) scale(1);
}

[data-aos=zoom-in] {
  transform: scale(.6);
}

[data-aos=zoom-in-up] {
  transform: translateY(100px) scale(.6);
}

[data-aos=zoom-in-down] {
  transform: translateY(-100px) scale(.6);
}

[data-aos=zoom-in-right] {
  transform: translate(-100px) scale(.6);
}

[data-aos=zoom-in-left] {
  transform: translate(100px) scale(.6);
}

[data-aos=zoom-out] {
  transform: scale(1.2);
}

[data-aos=zoom-out-up] {
  transform: translateY(100px) scale(1.2);
}

[data-aos=zoom-out-down] {
  transform: translateY(-100px) scale(1.2);
}

[data-aos=zoom-out-right] {
  transform: translate(-100px) scale(1.2);
}

[data-aos=zoom-out-left] {
  transform: translate(100px) scale(1.2);
}

[data-aos^=slide][data-aos^=slide] {
  transition-property: transform;
}

[data-aos^=slide][data-aos^=slide].aos-animate {
  transform: translate(0);
}

[data-aos=slide-up] {
  transform: translateY(100%);
}

[data-aos=slide-down] {
  transform: translateY(-100%);
}

[data-aos=slide-right] {
  transform: translateX(-100%);
}

[data-aos=slide-left] {
  transform: translateX(100%);
}

[data-aos^=flip][data-aos^=flip] {
  backface-visibility: hidden;
  transition-property: transform;
}

[data-aos=flip-left] {
  transform: perspective(2500px) rotateY(-100deg);
}

[data-aos=flip-left].aos-animate {
  transform: perspective(2500px) rotateY(0);
}

[data-aos=flip-right] {
  transform: perspective(2500px) rotateY(100deg);
}

[data-aos=flip-right].aos-animate {
  transform: perspective(2500px) rotateY(0);
}

[data-aos=flip-up] {
  transform: perspective(2500px) rotateX(-100deg);
}

[data-aos=flip-up].aos-animate {
  transform: perspective(2500px) rotateX(0);
}

[data-aos=flip-down] {
  transform: perspective(2500px) rotateX(100deg);
}

[data-aos=flip-down].aos-animate {
  transform: perspective(2500px) rotateX(0);
}



/* ===== assets/css/-Filterable-Gallery-with-Lightbox-BS4--Filterable-Gallery.css ===== */
.filtr-container {
  margin: 0 -4px;
}

.filtr-item {
  padding: 4px;
}

.filtr-controls {
  text-align: center;
  font-size: 18px;
  text-transform: uppercase;
  margin: 30px 0 15px;
}

.filtr-controls span {
  display: inline-block;
  margin: 10px 15px;
  cursor: pointer;
  padding: 5px 0;
  position: relative;
  color: #777;
}

.filtr-controls span:hover, .filtr-controls span.active {
  color: #000;
}

.filtr-controls span:after {
  content: ' ';
  width: 0;
  height: 2px;
  position: absolute;
  bottom: 0;
  left: 50%;
  background: transparent;
  -webkit-transition: all .6s ease;
  transition: all .6s ease;
}

.filtr-controls span.active:after {
  width: 100%;
  left: 0;
  background: #000;
}

@media (max-width:479px) {
  .filtr-item {
    width: 100%;
  }
}

.filtr-item > a {
  position: relative;
  display: block;
}

.filtr-item > a:before, .filtr-item > a:after {
  -webkit-transition: all .6s ease;
  transition: all .6s ease;
}

.filtr-item > a:before {
  content: ' ';
  position: absolute;
  z-index: 10;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  background: transparent;
}

.filtr-item:hover > a:before {
  background: rgba(0,0,0,.2);
}

.filtr-item > a:after {
  font-family: FontAwesome;
  content: '\f0fe';
  position: absolute;
  z-index: 11;
  top: 50%;
  left: 50%;
  margin: -20px 0 0 -17px;
  color: #fff;
  font-size: 40px;
  line-height: 40px;
  opacity: 0;
}

.filtr-item:hover > a:after {
  opacity: .5;
}

.filtr-loading {
  border: 5px solid #333;
  position: relative;
  border-radius: 40px;
  height: 40px;
  left: 50%;
  margin: 80px 0;
  opacity: 0;
  top: 0;
  width: 40px;
  z-index: 2;
  -webkit-animation: pulsate 1s ease-out infinite;
  -moz-animation: pulsate 1s ease-out infinite;
  -ms-animation: pulsate 1s ease-out infinite;
  -o-animation: pulsate 1s ease-out infinite;
  animation: pulsate 1s ease-out infinite;
}

.sl-wrapper .sl-close, .sl-wrapper .sl-navigation button {
  height: 44px;
  line-height: 44px;
  font-family: Arial,Baskerville,monospace;
}

.sl-wrapper .sl-close:focus, .sl-wrapper .sl-navigation button:focus {
  outline: 0;
}

body.hidden-scroll {
  overflow: hidden;
}

.sl-overlay {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background: #fff;
  /*opacity: .9;*/
  display: none;
  z-index: 1050;
}

.sl-wrapper .sl-close, .sl-wrapper .sl-counter {
  top: 30px;
  display: none;
  color: #000;
  position: fixed;
}

.sl-wrapper {
  z-index: 1040;
}

.sl-wrapper button {
  border: 0;
  background: 0 0;
  font-size: 28px;
  padding: 0;
  cursor: pointer;
}

.sl-wrapper button:hover {
  opacity: .7;
}

.sl-wrapper .sl-close {
  right: 30px;
  z-index: 1060;
  margin-top: -14px;
  margin-right: -14px;
  width: 44px;
  font-size: 5rem;
}

.sl-wrapper .sl-counter {
  left: 30px;
  z-index: 1060;
  font-size: 1rem;
}

.sl-wrapper .sl-navigation {
  width: 100%;
  display: none;
}

.sl-wrapper .sl-navigation button {
  position: fixed;
  top: 50%;
  margin-top: -22px;
  width: 22px;
  text-align: center;
  display: block;
  z-index: 1060;
  color: #000;
}

.sl-wrapper .sl-navigation button.sl-next {
  right: 5px;
  font-size: 2rem;
}

.sl-wrapper .sl-navigation button.sl-prev {
  left: 5px;
  font-size: 2rem;
}

.sl-wrapper .sl-image {
  position: fixed;
  -ms-touch-action: none;
  touch-action: none;
  z-index: 10000;
  /*border: 1px solid #ddd;*/
}

.sl-wrapper .sl-image img {
  margin: 0;
  padding: 15px;
  display: block;
  /*border: 0;*/
  -webkit-box-shadow: 0 0 15px rgba(0,0,0,.1);
  box-shadow: 0 0 15px rgba(0,0,0,.1);
  background: #fff;
}

.sl-wrapper .sl-image iframe {
  background: #000;
  border: 0;
}

@media (min-width:35.5em) {
  .sl-wrapper .sl-navigation button {
    width: 44px;
  }
}

@media (min-width:35.5em) {
  .sl-wrapper .sl-navigation button.sl-next {
    right: 10px;
    font-size: 4rem;
  }
}

@media (min-width:35.5em) {
  .sl-wrapper .sl-navigation button.sl-prev {
    left: 10px;
    font-size: 4rem;
  }
}

@media (min-width:35.5em) {
  .sl-wrapper .sl-image iframe, .sl-wrapper .sl-image img {
    border: 0;
  }
}

@media (min-width:50em) {
  .sl-wrapper .sl-navigation button {
    width: 44px;
  }
}

@media (min-width:50em) {
  .sl-wrapper .sl-navigation button.sl-next {
    right: 20px;
    font-size: 6rem;
  }
}

@media (min-width:50em) {
  .sl-wrapper .sl-navigation button.sl-prev {
    left: 20px;
    font-size: 6rem;
  }
}

@media (min-width:50em) {
  .sl-wrapper .sl-image iframe, .sl-wrapper .sl-image img {
    border: 0;
  }
}

.sl-wrapper .sl-image .sl-caption {
  display: none;
  padding: 10px;
  color: #fff;
  background: rgba(0,0,0,.8);
  position: absolute;
  bottom: 15px;
  left: 15px;
  right: 15px;
  text-align: center;
  /*-webkit-animation: opacity .6s ease-out;*/
  /*-moz-animation: opacity .6s ease-out;*/
  /*-ms-animation: opacity .6s ease-out;*/
  /*-o-animation: opacity .6s ease-out;*/
  /*animation: opacity .6s ease-out;*/
}

.sl-wrapper .sl-image .sl-caption.pos-top {
  bottom: auto;
  top: 15px;
}

.sl-wrapper .sl-image .sl-caption.pos-outside {
  bottom: auto;
}

.sl-wrapper .sl-image .sl-download {
  display: none;
  position: absolute;
  bottom: 5px;
  right: 5px;
  color: #000;
  z-index: 1060;
}

.sl-spinner {
  display: none;
  border: 5px solid #333;
  border-radius: 40px;
  height: 40px;
  left: 50%;
  margin: -20px 0 0 -20px;
  opacity: 0;
  position: fixed;
  top: 50%;
  width: 40px;
  z-index: 1057;
  -webkit-animation: pulsate 1s ease-out infinite;
  -moz-animation: pulsate 1s ease-out infinite;
  -ms-animation: pulsate 1s ease-out infinite;
  -o-animation: pulsate 1s ease-out infinite;
  animation: pulsate 1s ease-out infinite;
}

.sl-scrollbar-measure {
  position: absolute;
  top: -9999px;
  width: 50px;
  height: 50px;
  overflow: scroll;
}

@keyframes pulsate {
}

@keyframes opacity {
}



/* ===== assets/css/Lightbox-Gallery-baguetteBox.min.css ===== */
/* !
 * baguetteBox.js
 * @author  feimosi
 * @version 1.11.1
 * @url https://github.com/feimosi/baguetteBox.js */



/* ===== assets/css/Lightbox-Gallery-No-Gutters-baguetteBox.min.css ===== */
/* !
 * baguetteBox.js
 * @author  feimosi
 * @version 1.11.1
 * @url https://github.com/feimosi/baguetteBox.js */

#baguetteBox-overlay {
  display: none;
  opacity: 0;
  position: fixed;
  overflow: hidden;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1000000;
  background-color: #222;
  background-color: rgba(0,0,0,.8);
  -webkit-transition: opacity .5s ease;
  transition: opacity .5s ease;
}

#baguetteBox-overlay.visible {
  opacity: 1;
}

#baguetteBox-overlay .full-image {
  display: inline-block;
  position: relative;
  width: 100%;
  height: 100%;
  text-align: center;
}

#baguetteBox-overlay .full-image figure {
  display: inline;
  margin: 0;
  height: 100%;
}

#baguetteBox-overlay .full-image img {
  display: inline-block;
  width: auto;
  height: auto;
  max-height: 100%;
  max-width: 100%;
  vertical-align: middle;
  -webkit-box-shadow: 0 0 8px rgba(0,0,0,.6);
  -moz-box-shadow: 0 0 8px rgba(0,0,0,.6);
  box-shadow: 0 0 8px rgba(0,0,0,.6);
}

#baguetteBox-overlay .full-image figcaption {
  display: block;
  position: absolute;
  bottom: 0;
  width: 100%;
  text-align: center;
  line-height: 1.8;
  white-space: normal;
  color: #ccc;
  background-color: #000;
  background-color: rgba(0,0,0,.6);
  font-family: sans-serif;
}

#baguetteBox-overlay .full-image:before {
  content: "";
  display: inline-block;
  height: 50%;
  width: 1px;
  margin-right: -1px;
}

#baguetteBox-slider {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  white-space: nowrap;
  -webkit-transition: left .4s ease,-webkit-transform .4s ease;
  transition: left .4s ease,-webkit-transform .4s ease;
  transition: left .4s ease,transform .4s ease;
  transition: left .4s ease,transform .4s ease,-webkit-transform .4s ease,-moz-transform .4s ease;
}

#baguetteBox-slider.bounce-from-right {
  -webkit-animation: bounceFromRight .4s ease-out;
  animation: bounceFromRight .4s ease-out;
}

#baguetteBox-slider.bounce-from-left {
  -webkit-animation: bounceFromLeft .4s ease-out;
  animation: bounceFromLeft .4s ease-out;
}

@keyframes bounceFromRight {
  0%, 100% {
    margin-left: 0;
  }
  50% {
    margin-left: -30px;
  }
}

@keyframes bounceFromLeft {
  0%, 100% {
    margin-left: 0;
  }
  50% {
    margin-left: 30px;
  }
}

.baguetteBox-button#next-button, .baguetteBox-button#previous-button {
  top: 50%;
  top: calc(50% - 30px);
  width: 44px;
  height: 60px;
}

.baguetteBox-button {
  position: absolute;
  cursor: pointer;
  outline: 0;
  padding: 0;
  margin: 0;
  border: 0;
  -moz-border-radius: 15%;
  border-radius: 15%;
  background-color: #323232;
  background-color: rgba(50,50,50,.5);
  color: #ddd;
  font: 1.6em sans-serif;
  -webkit-transition: background-color .4s ease;
  transition: background-color .4s ease;
}

.baguetteBox-button:focus, .baguetteBox-button:hover {
  background-color: rgba(50,50,50,.9);
}

.baguetteBox-button#next-button {
  right: 2%;
}

.baguetteBox-button#previous-button {
  left: 2%;
}

.baguetteBox-button#close-button {
  top: 20px;
  right: 2%;
  right: calc(2% + 6px);
  width: 30px;
  height: 30px;
}

.baguetteBox-button svg {
  position: absolute;
  left: 0;
  top: 0;
}

.baguetteBox-spinner {
  width: 40px;
  height: 40px;
  display: inline-block;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -20px;
  margin-left: -20px;
}

.baguetteBox-double-bounce1, .baguetteBox-double-bounce2 {
  width: 100%;
  height: 100%;
  -moz-border-radius: 50%;
  border-radius: 50%;
  background-color: #fff;
  opacity: .6;
  position: absolute;
  top: 0;
  left: 0;
  -webkit-animation: bounce 2s infinite ease-in-out;
  animation: bounce 2s infinite ease-in-out;
}

.baguetteBox-double-bounce2 {
  -webkit-animation-delay: -1s;
  animation-delay: -1s;
}

@keyframes bounce {
  0%, 100% {
    -webkit-transform: scale(0);
    -moz-transform: scale(0);
    transform: scale(0);
  }
  50% {
    -webkit-transform: scale(1);
    -moz-transform: scale(1);
    transform: scale(1);
  }
}



/* ===== assets/css/Simple-Slider-swiper-bundle.min.css ===== */
/* *
 * Swiper 6.4.8
 * Most modern mobile touch slider and framework with hardware accelerated transitions
 * https://swiperjs.com
 *
 * Copyright 2014-2021 Vladimir Kharlampidi
 *
 * Released under the MIT License
 *
 * Released on: January 22, 2021 */

:root {
  --swiper-theme-color: #007aff;
}

.swiper-container {
  margin-left: auto;
  margin-right: auto;
  position: relative;
  overflow: hidden;
  list-style: none;
  padding: 0;
  z-index: 1;
}

.swiper-wrapper {
  position: relative;
  width: 100%;
  height: 100%;
  z-index: 1;
  display: flex;
  transition-property: transform;
  box-sizing: content-box;
}

.swiper-container-android .swiper-slide, .swiper-wrapper {
  transform: translate3d(0px,0,0);
}

:root {
  --swiper-navigation-size: 44px;
}

.swiper-button-next, .swiper-button-prev {
  position: absolute;
  top: 50%;
  width: calc(var(--swiper-navigation-size)/ 44 * 27);
  height: var(--swiper-navigation-size);
  margin-top: calc(-1 * var(--swiper-navigation-size)/ 2);
  z-index: 10;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--swiper-navigation-color,var(--swiper-theme-color));
}

.swiper-button-next:after, .swiper-button-prev:after {
  font-family: swiper-icons;
  font-size: var(--swiper-navigation-size);
  text-transform: none!important;
  letter-spacing: 0;
  text-transform: none;
  font-variant: initial;
  line-height: 1;
}

.swiper-button-prev, .swiper-container-rtl .swiper-button-next {
  left: 10px;
  right: auto;
}

.swiper-button-prev:after, .swiper-container-rtl .swiper-button-next:after {
  content: 'prev';
}

.swiper-button-next, .swiper-container-rtl .swiper-button-prev {
  right: 10px;
  left: auto;
}

.swiper-button-next:after, .swiper-container-rtl .swiper-button-prev:after {
  content: 'next';
}

.swiper-pagination {
  position: absolute;
  text-align: center;
  transition: .3s opacity;
  transform: translate3d(0,0,0);
  z-index: 10;
}



/* ===== assets/css/Simple-Slider.css ===== */
.simple-slider .swiper-button-next, .simple-slider .swiper-button-prev {
  width: 50px;
  margin-left: 20px;
  margin-right: 20px;
}

@media (max-width:767px) {
  .simple-slider .swiper-button-next, .simple-slider .swiper-button-prev {
    display: none;
  }
}



/* ===== assets/css/theme-color-fix.css ===== */
/**
 * THEME COLOR FIX
 * ===============
 * Fixes the systemic bug where `var(--bs-body-bg)` was used as a TEXT color
 * across 20+ CSS files. `--bs-body-bg` is the PAGE BACKGROUND color:
 *   - Light mode: #ffffff (white) → white text on white = INVISIBLE
 *   - Dark mode: #212529 (dark) → dark text on dark = INVISIBLE
 *
 * This file introduces a proper `--re-text-color` variable and overrides all
 * the broken selectors to use it. It also ensures admin pages never show
 * white-on-white or dark-on-dark text.
 *
 * Load this file AFTER all other theme CSS files.
 */

/* ============================================
   1. DEFINE CORRECT TEXT COLOR VARIABLES
   ============================================ */

:root {
    /* Standard content text - always light on our dark backgrounds */
    --re-text-color: rgba(255, 255, 255, 0.92);
    --re-text-muted: rgba(255, 255, 255, 0.65);
    --re-text-subtle: rgba(255, 255, 255, 0.45);

    /* Heading text */
    --re-heading-color: #ffffff;

    /* Dedicated accent color - bright lavender/purple for dark backgrounds */
    --re-accent-color: var(--re-brand-200);

    /* Admin-specific text */
    --re-admin-text: rgba(255, 255, 255, 0.92);
    --re-admin-text-muted: rgba(255, 255, 255, 0.6);
}

/* Light theme overrides */
[data-bs-theme="light"] {
    /* Keep default text light since the body background remains a dark purple gradient in both modes */
    --re-text-color: rgba(255, 255, 255, 0.92);
    --re-text-muted: rgba(255, 255, 255, 0.65);
    --re-text-subtle: rgba(255, 255, 255, 0.45);
    --re-heading-color: #ffffff;
    
    /* Slightly darker accent color for light theme text readability */
    --re-accent-color: var(--re-brand-400);
}

/* ============================================
   2. FIX ADMIN THEME TEXT COLORS
   Overrides admin-theme.css where var(--bs-body-bg)
   was incorrectly used for text color
   ============================================ */

/* Admin page header description */
.admin-page-header p {
    color: var(--re-text-color) !important;
    opacity: 0.8;
}

/* Admin breadcrumb */
.admin-breadcrumb .breadcrumb-item.active {
    color: var(--re-text-color) !important;
}

.admin-breadcrumb .breadcrumb-item a:hover {
    color: var(--re-text-color) !important;
}

/* Admin secondary button text */
.admin-btn-secondary {
    color: var(--re-text-color) !important;
}

.admin-btn-secondary:hover {
    color: var(--re-text-color) !important;
}

/* Admin form controls */
.admin-form-control,
.admin-form-control:focus {
    color: var(--re-text-color) !important;
}

.admin-form-select {
    color: var(--re-text-color) !important;
}

.admin-form-text {
    color: var(--re-text-muted) !important;
    opacity: 1;
}

/* Admin tables */
.admin-table th,
.admin-table td {
    color: var(--re-text-color) !important;
}

/* Admin stat labels */
.admin-stat-label {
    color: var(--re-text-muted) !important;
    opacity: 1;
}

/* Admin alerts */
.admin-alert-success,
.admin-alert-danger,
.admin-alert-warning,
.admin-alert-info {
    color: var(--re-text-color) !important;
}

/* Admin list items */
.admin-list-item,
.admin-list-item:hover {
    color: var(--re-text-color) !important;
}

/* Admin loading/empty states */
.admin-loading,
.admin-empty-state {
    color: var(--re-text-muted) !important;
    opacity: 1;
}

/* Admin text utilities */
.admin-text-muted {
    color: var(--re-text-muted) !important;
    opacity: 1;
}

/* ============================================
   3. FIX ADMIN GLASS THEME TEXT COLORS
   Overrides admin-glass-theme.css
   ============================================ */

/* Form inputs in admin */
body[data-page*="admin"] .form-control,
body[data-page*="admin"] .form-select,
body[data-page*="admin"] input[type="text"],
body[data-page*="admin"] input[type="email"],
body[data-page*="admin"] input[type="password"],
body[data-page*="admin"] input[type="number"],
body[data-page*="admin"] textarea,
body[data-page*="admin"] select {
    color: var(--re-admin-text) !important;
}

/* Pagination in admin */
body[data-page*="admin"] .pagination .page-item .page-link {
    color: var(--re-admin-text) !important;
}

/* ============================================
   4. FIX BOOTSTRAP BLUE FIXES TEXT COLORS
   Overrides bootstrap-blue-fixes.css
   ============================================ */

/* Server info footer */
.server-info-footer,
.server-info-footer * {
    color: rgba(255, 255, 255, 0.85) !important;
}

footer[style*="position: fixed"] * {
    color: rgba(255, 255, 255, 0.85) !important;
}

/* Analytics elements */
#map-stats,
#map-stats *,
.analytics-table,
.analytics-table td {
    color: var(--re-text-color) !important;
}

.top-locations,
.top-locations * {
    color: var(--re-text-color) !important;
}

/* Stat cards */
.col-md-2 > div[style*="background"],
.col-md-3 > div[style*="background"] {
    color: var(--re-text-color) !important;
}

/* Chart elements */
.chart-legend,
.chart-label {
    color: var(--re-text-color) !important;
}

/* Tooltips */
.tooltip {
    color: var(--re-text-color) !important;
}

/* SAFELY TARGET ELEMENTS WITH THE INCORRECT INLINE COLOR */
/*
 * This targeted attribute selector safely redirects any inline 'color: var(--re-text-color)' 
 * styling to use our high-contrast '--re-text-color' instead. Unlike the destructive '*' 
 * nuclear selector, this only matches elements where the background token was incorrectly 
 * used as text color, preserving all assigned purple/gold colors, links, headings, and states!
 */
[style*="color: var(--re-text-color)"],
[style*="color:var(--re-text-color)"],
[style*="color: var(--re-text-color) !important"],
[style*="color:var(--re-text-color)!important"],
[style*="color: var(--bs-body-color)"],
[style*="color:var(--bs-body-color)"] {
    color: var(--re-text-color) !important;
}

/* ============================================
   5. FIX MAIN-THEME.CSS ARTICLE TEXT
   ============================================ */

.article-container-dark p,
.article-container-light p {
    color: var(--re-text-color) !important;
}

/* ============================================
   6. FIX ADMIN DASHBOARD TEXT
   Ensure ALL admin page text is visible
   ============================================ */

/* Global admin body text */
body[data-page*="admin"] {
    color: var(--re-admin-text);
}

/* All text inside admin containers */
body[data-page*="admin"] h1:not(.adm-callout-title),
body[data-page*="admin"] h2:not(.adm-callout-title),
body[data-page*="admin"] h3:not(.adm-callout-title),
body[data-page*="admin"] h4:not(.adm-callout-title),
body[data-page*="admin"] h5:not(.adm-callout-title),
body[data-page*="admin"] h6:not(.adm-callout-title) {
    color: var(--re-heading-color);
}

body[data-page*="admin"] p,
body[data-page*="admin"] span:not(.badge):not(.text-primary):not(.text-danger):not(.text-warning):not(.text-success):not(.text-info),
body[data-page*="admin"] div:not(.badge),
body[data-page*="admin"] td,
body[data-page*="admin"] th,
body[data-page*="admin"] li,
body[data-page*="admin"] label,
body[data-page*="admin"] .form-label {
    color: var(--re-admin-text);
}

/*
 * Admin links should use theme purple - but a link that is dressed as a
 * button is not a link for this purpose.
 *
 * .adm-btn joins .btn in the exclusions for exactly the reason .btn is
 * already there. Without it this rule repainted every button in the converted
 * admin pages: the specificity here is four classes deep, so it beat
 * .adm-btn-primary's own colour and left white-on-slate buttons rendering as
 * grey text on a grey ground - unreadable, and invisible in a diff.
 */
body[data-page*="admin"] a:not(.btn):not(.adm-btn):not(.adm-tab):not(.adm-nav-item):not(.nav-link):not(.dropdown-item) {
    color: var(--re-brand-400);
}

body[data-page*="admin"] a:not(.btn):not(.adm-btn):not(.adm-tab):not(.adm-nav-item):not(.nav-link):not(.dropdown-item):hover {
    color: var(--re-brand-300);
}

/* Ensure table headers are prominent */
body[data-page*="admin"] table thead th {
    color: var(--re-heading-color) !important;
    font-weight: 600;
}

body[data-page*="admin"] table tbody td {
    color: var(--re-admin-text) !important;
}

/* Admin stat numbers */
body[data-page*="admin"] .admin-stat-number,
body[data-page*="admin"] [style*="font-size: 2"] {
    color: var(--re-heading-color) !important;
}

/* ============================================
   7. FIX ADMIN DASHBOARD TABLE FIX TEXT
   ============================================ */

.visitors-table-section,
.visitors-table-section * {
    color: var(--re-admin-text);
}

.visitors-table-section table td,
.visitors-table-section table th {
    color: var(--re-admin-text) !important;
}

.auto-resolve-panel,
.auto-resolve-panel * {
    color: var(--re-admin-text);
}

/* ============================================
   8. ENSURE BADGES KEEP THEIR COLORS
   (Don't override semantic color classes)
   ============================================ */

.text-success { color: #28a745 !important; }
.text-danger { color: #dc3545 !important; }
.text-warning { color: #ffc107 !important; }
.text-info { color: #17a2b8 !important; }
.admin-text-primary { color: var(--re-brand-400) !important; }
.admin-text-success { color: #28a745 !important; }
.admin-text-danger { color: #dc3545 !important; }
.admin-text-warning { color: #ffc107 !important; }

/* Badge text should be white for standard Bootstrap categories (or black for warning) */
.badge:not(.glass):not(.glass-light):not(.badge-outline) { color: white !important; }
.badge.bg-warning,
.badge.admin-badge-warning,
.admin-badge-warning { color: #000 !important; }

/* ============================================
   9. VISITORS TABLE PAGINATION
   ============================================ */

.pagination-controls a,
.pagination-controls span {
    color: var(--re-admin-text) !important;
}

.pagination-controls a:hover {
    color: var(--re-brand-300) !important;
}

/* ============================================
   10. SYSTEMIC BODY TEXT LEGIBILITY & GLASS PANEL THEMING
   ============================================ */

/* Set default frontend body text to high-contrast white since the background is dark */
body:not([data-page*="admin"]) {
    color: rgba(255, 255, 255, 0.92) !important;
    --re-accent-color: #d1b3c4;
}

[data-bs-theme="light"] {
    --re-accent-color: var(--re-brand-400);
}

/* Make sure headings are white on the dark background by default */
body:not([data-page*="admin"]) h1,
body:not([data-page*="admin"]) h2,
body:not([data-page*="admin"]) h3,
body:not([data-page*="admin"]) h4,
body:not([data-page*="admin"]) h5,
body:not([data-page*="admin"]) h6 {
    color: #ffffff;
}

/* Inside light-theme frosted white glass containers, switch text to highly legible dark gray/black */
[data-bs-theme="light"] .glass,
[data-bs-theme="light"] .glass-container,
[data-bs-theme="light"] .card,
[data-bs-theme="light"] .project-card,
[data-bs-theme="light"] .article-card,
[data-bs-theme="light"] .game-card,
[data-bs-theme="light"] .skill-category,
[data-bs-theme="light"] .experience-item,
[data-bs-theme="light"] .experience-card,
[data-bs-theme="light"] .education-card,
[data-bs-theme="light"] .recommendation-card {
    --re-text-color: rgba(0, 0, 0, 0.87);
    --re-text-muted: rgba(0, 0, 0, 0.6);
    --re-text-subtle: rgba(0, 0, 0, 0.4);
    --re-heading-color: #1a1a1a;
    background: rgba(255, 255, 255, 0.45) !important;
    border-color: rgba(0, 0, 0, 0.15) !important;
    box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.08) !important;
    color: rgba(0, 0, 0, 0.87) !important;
}

/* Inside light-theme white glass headings, force dark color for readability */
[data-bs-theme="light"] .glass h1,
[data-bs-theme="light"] .glass h2,
[data-bs-theme="light"] .glass h3,
[data-bs-theme="light"] .glass h4,
[data-bs-theme="light"] .glass h5,
[data-bs-theme="light"] .glass h6,
[data-bs-theme="light"] .glass-container h1,
[data-bs-theme="light"] .glass-container h2,
[data-bs-theme="light"] .glass-container h3,
[data-bs-theme="light"] .glass-container h4,
[data-bs-theme="light"] .glass-container h5,
[data-bs-theme="light"] .glass-container h6,
[data-bs-theme="light"] .card h1,
[data-bs-theme="light"] .card h2,
[data-bs-theme="light"] .card h3,
[data-bs-theme="light"] .card h4,
[data-bs-theme="light"] .card h5,
[data-bs-theme="light"] .card h6,
[data-bs-theme="light"] .skill-category h1,
[data-bs-theme="light"] .skill-category h2,
[data-bs-theme="light"] .skill-category h3,
[data-bs-theme="light"] .skill-category h4,
[data-bs-theme="light"] .skill-category h5,
[data-bs-theme="light"] .skill-category h6 {
    color: #1a1a1a !important;
}

/* Ensure the navigation bar text is ALWAYS light and highly visible since the navbar is always dark */
.navbar,
.navbar * {
    --re-text-color: rgba(255, 255, 255, 0.9) !important;
    --re-heading-color: #ffffff !important;
    --bs-navbar-color: rgba(255, 255, 255, 0.7) !important;
    --bs-navbar-hover-color: #ffffff !important;
    --bs-navbar-active-color: #ffffff !important;
    --bs-navbar-brand-color: #ffffff !important;
    --bs-navbar-brand-hover-color: #ffffff !important;
}

.navbar .nav-link {
    /* --re-text-color, not a bare white: the same ink the header strip and
       the footer use, so the three read as one piece of chrome. */
    color: var(--re-text-color) !important;
}

.navbar .nav-link:hover,
.navbar .nav-link.active {
    color: #ffffff !important;
}

.navbar .navbar-brand,
.navbar .navbar-brand * {
    color: #ffffff !important;
}

.navbar .header-controls-panel a,
.navbar .header-controls-panel span,
.navbar .header-controls-panel button {
    color: rgba(255, 255, 255, 0.8) !important;
}

.navbar .header-controls-panel a:hover {
    color: #ffffff !important;
}

/* Glass Badge Legibility Overrides */
.badge.glass,
.badge.glass-light {
    font-weight: 600 !important;
    letter-spacing: 0.5px !important;
    text-shadow: none !important;
}

/* In Dark Mode (default) */
body:not([data-bs-theme="light"]) .badge.glass,
body:not([data-bs-theme="light"]) .badge.glass-light {
    background: var(--re-surface-sunken) !important;
    color: var(--re-brand-100) !important; /* Extremely high contrast light lavender-white */
    border: 1px solid rgba(130, 107, 125, 0.45) !important;
}

/* In Light Mode */
[data-bs-theme="light"] .badge.glass,
[data-bs-theme="light"] .badge.glass-light {
    background: var(--re-surface-raised) !important; /* Frosted lavender */
    color: #4a3847 !important; /* Dark purple/gray for ultimate legibility */
    border: 1px solid var(--re-border) !important;
}

/* Resume Page Specific Color Fixes to bypass hijacked variables */
body:not([data-page*="admin"]) .resume-header,
body:not([data-page*="admin"]) .resume-section,
body:not([data-page*="admin"]) .experience-card,
body:not([data-page*="admin"]) .education-card,
body:not([data-page*="admin"]) .skill-category,
body:not([data-page*="admin"]) .recommendation-card {
    --bs-link-hover-color: var(--re-accent-color) !important;
    --bs-link-color: var(--re-accent-color) !important;
}

/* ============================================
   RESUME PAGE - MUSIC PLAYER DARK GLASS STYLING
   ============================================ */
body:not([data-page*="admin"]) .resume-header .glass-container,
body:not([data-page*="admin"]) .experience-card.glass,
body:not([data-page*="admin"]) .education-card.glass,
body:not([data-page*="admin"]) .skill-category.glass,
body:not([data-page*="admin"]) .recommendation-card.glass {
    background: var(--re-surface) !important;
    backdrop-filter: var(--re-glass) !important;
    -webkit-backdrop-filter: var(--re-glass) !important;
    
    border-radius: var(--re-radius-md) !important;
    border: 1px solid var(--re-border) !important;
    box-shadow: 
        0 8px 32px 0 rgba(0,0,0,0.5),
        inset 0 1px 0 0 rgba(255,255,255,0.18),
        inset 0 -1px 0 0 rgba(0,0,0,0.15) !important;
    
    position: relative !important;
    overflow: hidden !important;
}

/* Injected Glass noise texture overlay to match the music player */
body:not([data-page*="admin"]) .resume-header .glass-container::before,
body:not([data-page*="admin"]) .experience-card.glass::before,
body:not([data-page*="admin"]) .education-card.glass::before,
body:not([data-page*="admin"]) .skill-category.glass::before,
body:not([data-page*="admin"]) .recommendation-card.glass::before {
    content: '' !important;
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    width: 100% !important;
    height: 100% !important;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilterExpanded'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilterExpanded)'/%3E%3C/svg%3E") !important;
    opacity: 0.06 !important;
    pointer-events: none !important;
    z-index: 1 !important;
    border-radius: var(--re-radius-md) !important;
}

/* Keep the card elements above the noise texture */
body:not([data-page*="admin"]) .resume-header .glass-container > *,
body:not([data-page*="admin"]) .experience-card.glass > *,
body:not([data-page*="admin"]) .education-card.glass > *,
body:not([data-page*="admin"]) .skill-category.glass > *,
body:not([data-page*="admin"]) .recommendation-card.glass > * {
    position: relative !important;
    z-index: 2 !important;
}

/* ============================================
   RESUME PAGE - LEGIbILITY CONTRAST GUARANTEE
   Ensures text remains white/light lavender even when
   the global [data-bs-theme="light"] overrides are active!
   ============================================ */
body:not([data-page*="admin"]) .resume-header .glass-container,
body:not([data-page*="admin"]) .experience-card.glass,
body:not([data-page*="admin"]) .education-card.glass,
body:not([data-page*="admin"]) .skill-category.glass,
body:not([data-page*="admin"]) .recommendation-card.glass {
    --re-text-color: rgba(255, 255, 255, 0.92) !important;
    --re-text-muted: rgba(255, 255, 255, 0.65) !important;
    --re-text-subtle: rgba(255, 255, 255, 0.45) !important;
    --re-heading-color: #ffffff !important;
    --re-accent-color: var(--re-brand-200) !important; /* Force a bright light lavender accent in both modes */
    color: rgba(255, 255, 255, 0.92) !important;
}

/* Force headings inside these dark glass containers to be white/light */
body:not([data-page*="admin"]) .resume-header .glass-container h1,
body:not([data-page*="admin"]) .resume-header .glass-container h2,
body:not([data-page*="admin"]) .resume-header .glass-container h3,
body:not([data-page*="admin"]) .resume-header .glass-container h4,
body:not([data-page*="admin"]) .resume-header .glass-container h5,
body:not([data-page*="admin"]) .resume-header .glass-container h6,
body:not([data-page*="admin"]) .experience-card.glass h1,
body:not([data-page*="admin"]) .experience-card.glass h2,
body:not([data-page*="admin"]) .experience-card.glass h3,
body:not([data-page*="admin"]) .experience-card.glass h4,
body:not([data-page*="admin"]) .experience-card.glass h5,
body:not([data-page*="admin"]) .experience-card.glass h6,
body:not([data-page*="admin"]) .education-card.glass h1,
body:not([data-page*="admin"]) .education-card.glass h2,
body:not([data-page*="admin"]) .education-card.glass h3,
body:not([data-page*="admin"]) .education-card.glass h4,
body:not([data-page*="admin"]) .education-card.glass h5,
body:not([data-page*="admin"]) .education-card.glass h6,
body:not([data-page*="admin"]) .skill-category.glass h1,
body:not([data-page*="admin"]) .skill-category.glass h2,
body:not([data-page*="admin"]) .skill-category.glass h3,
body:not([data-page*="admin"]) .skill-category.glass h4,
body:not([data-page*="admin"]) .skill-category.glass h5,
body:not([data-page*="admin"]) .skill-category.glass h6,
body:not([data-page*="admin"]) .recommendation-card.glass h1,
body:not([data-page*="admin"]) .recommendation-card.glass h2,
body:not([data-page*="admin"]) .recommendation-card.glass h3,
body:not([data-page*="admin"]) .recommendation-card.glass h4,
body:not([data-page*="admin"]) .recommendation-card.glass h5,
body:not([data-page*="admin"]) .recommendation-card.glass h6 {
    color: #ffffff !important;
}

/* Force standard paragraphs, bold texts, lists, icons, and inline text to be light color */
body:not([data-page*="admin"]) .resume-header .glass-container p,
body:not([data-page*="admin"]) .resume-header .glass-container strong,
body:not([data-page*="admin"]) .resume-header .glass-container span,
body:not([data-page*="admin"]) .resume-header .glass-container a:not(.btn),
body:not([data-page*="admin"]) .resume-header .glass-container i,
body:not([data-page*="admin"]) .experience-card.glass p,
body:not([data-page*="admin"]) .experience-card.glass strong,
body:not([data-page*="admin"]) .experience-card.glass span,
body:not([data-page*="admin"]) .experience-card.glass li,
body:not([data-page*="admin"]) .experience-card.glass i,
body:not([data-page*="admin"]) .education-card.glass p,
body:not([data-page*="admin"]) .education-card.glass strong,
body:not([data-page*="admin"]) .education-card.glass span,
body:not([data-page*="admin"]) .education-card.glass li,
body:not([data-page*="admin"]) .education-card.glass i,
body:not([data-page*="admin"]) .skill-category.glass p,
body:not([data-page*="admin"]) .skill-category.glass strong,
body:not([data-page*="admin"]) .skill-category.glass span,
body:not([data-page*="admin"]) .skill-category.glass i,
body:not([data-page*="admin"]) .recommendation-card.glass p,
body:not([data-page*="admin"]) .recommendation-card.glass strong,
body:not([data-page*="admin"]) .recommendation-card.glass span,
body:not([data-page*="admin"]) .recommendation-card.glass i {
    color: rgba(255, 255, 255, 0.92) !important;
}

/* Specific calendar dates and location labels overrides */
body:not([data-page*="admin"]) .experience-card.glass .badge.glass-light span,
body:not([data-page*="admin"]) .education-card.glass .badge.glass-light span {
    color: var(--re-brand-100) !important;
}

/* ==============================================================================
   THALEAHFAT TITLES & HEADINGS ENFORCEMENT
   ============================================================================== */

/* Apply ThaleahFat to all heading levels and titles across the site */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6,
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a,
.h1 a, .h2 a, .h3 a, .h4 a, .h5 a, .h6 a,
h1[style], h2[style], h3[style], h4[style], h5[style], h6[style],
.section-title, .section-title a,
.post-title, .post-title a,
.article-title, .article-title a,
.portfolio-title, .portfolio-title a,
.page-title, .page-title a,
.card-title, .card-title a,
.resume-section-title,
.skill-category-title,
h1[style*="Silkscreen"], 
h2[style*="Silkscreen"], 
h3[style*="Silkscreen"], 
h4[style*="Silkscreen"], 
h5[style*="Silkscreen"], 
h6[style*="Silkscreen"],
h1[style*="Albert Sans"], 
h2[style*="Albert Sans"], 
h3[style*="Albert Sans"], 
h4[style*="Albert Sans"], 
h5[style*="Albert Sans"], 
h6[style*="Albert Sans"],
[style*="font-family: 'Silkscreen'"] h1,
[style*="font-family: 'Silkscreen'"] h2,
[style*="font-family: 'Silkscreen'"] h3,
[style*="font-family: 'Silkscreen'"] h4,
[style*="font-family: 'Silkscreen'"] h5,
[style*="font-family: 'Silkscreen'"] h6 {
    font-family: var(--re-font-display) !important;
    font-weight: normal !important; /* Pixel fonts are pre-bolded; bolding causes browser smearing */
    -webkit-font-smoothing: none !important;
    -moz-osx-font-smoothing: grayscale !important;
    font-smooth: never !important;
    text-rendering: optimizeSpeed !important;
}

/* Apply ThaleahFat to the Main Website Logo / Navbar Branding */
.navbar-brand,
.navbar-brand *,
.navbar-brand span,
.navbar-brand a,
[class*="navbar-brand"],
[class*="navbar-brand"] * {
    font-family: var(--re-font-display) !important;
    font-weight: normal !important;
    -webkit-font-smoothing: none !important;
    -moz-osx-font-smoothing: grayscale !important;
    font-smooth: never !important;
    text-rendering: optimizeSpeed !important;
}

/*
 * Polish size scale for retro-pixel headings.
 *
 * DO NOT carve components out of this with :not(). It was tried, and it
 * backfired: every :not(.class) adds a class to the selector's specificity,
 * so after six of them this rule stood at (0,6,1) and started beating
 * selectors that had nothing to do with the admin - silkscreen-pixel-grid.css
 * registers `.cv-skill-cat > h3` at (0,1,1) to hold it at 8px, and the resume
 * skill headings came out as 25.6px Silkscreen. `.theme-section h4` and
 * `.filter-section-title` were losing the same way.
 *
 * It is a bare tag selector again, at (0,0,1), which any single class beats.
 * A heading component that wants its own size declares it !important in its
 * own file; the pixel grid, which loads last, wins on its own registrations.
 */
h1,
.h1 { font-size: 2.5rem !important; }
h2,
.h2 { font-size: 2.0rem !important; }
h3,
.h3 { font-size: 1.6rem !important; }
h4,
.h4 { font-size: 1.3rem !important; }
h5,
.h5 { font-size: 1.15rem !important; }
h6,
.h6 { font-size: 1.0rem !important; }

/* Explicit override to force readable retro size on Skill Category headings (backend, engines, design, tools, etc.) */
.skill-category h3,
.skill-category h3[style],
.skill-category h3[style*="Silkscreen"],
body:not([data-page*="admin"]) .skill-category.glass h3 {
    font-size: 1.8rem !important;
    font-family: var(--re-font-display) !important;
    letter-spacing: 1px !important;
}

/* ============================================
   AUTH CARDS — UNIFIED DARK GLASS
   login/register/forgot-password/reset-password previously diverged:
   login was hardcoded dark (rgba(0,0,0,0.5)) while the others used the
   theme-aware glass variable, which turns LIGHT in light mode. The result
   was a dark login card next to light register/forgot cards. This forces
   ONE consistent dark refractive-glass card on all four, in both themes.
   The doubled `.container.py-4.py-xl-5.auth-card` selector (4 classes) and
   this file loading last guarantee it wins over the glass-standard rule.
   ============================================ */
.auth-card,
.container.py-4.py-xl-5.auth-card {
    width: 100% !important;
    max-width: 520px !important;
    margin-left: auto !important;
    margin-right: auto !important;
    padding: 32px !important;
    background: rgba(20, 15, 22, 0.62) !important;
    backdrop-filter: var(--re-glass) !important;
    -webkit-backdrop-filter: var(--re-glass) !important;
    border: 1px solid var(--re-border) !important;
    border-radius: var(--re-radius-lg) !important;
    box-shadow:
        0 8px 32px 0 rgba(0, 0, 0, 0.5),
        inset 0 1px 0 0 rgba(255, 255, 255, 0.18),
        inset 0 -1px 0 0 rgba(0, 0, 0, 0.15) !important;
    position: relative !important;
    overflow: hidden !important;
}

/* Noise texture overlay (matches every other glass surface) */
.auth-card::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseAuth'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4.5' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseAuth)'/%3E%3C/svg%3E");
    opacity: 0.07;
    pointer-events: none;
    z-index: 1;
    border-radius: inherit;
}

/* Rim-light highlight on the top edge */
.auth-card::after {
    content: '';
    position: absolute;
    top: 1px;
    left: 8px;
    right: 8px;
    height: 1px;
    background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.3) 50%, transparent 100%);
    pointer-events: none;
    z-index: 2;
}

/* Keep content above the texture/rim layers */
.auth-card > * {
    position: relative;
    z-index: 3;
}

/* The card stays dark in both themes, so its text must stay light even when
   the global light-theme rules would otherwise darken glass-card text. */
[data-bs-theme="light"] .auth-card,
[data-bs-theme="light"] .auth-card * {
    --re-text-color: rgba(255, 255, 255, 0.92);
}





/* ==============================================================================
   UNIFIED CONTROLS

   Buttons had drifted into five unrelated treatments: .btn-primary-standard was
   a solid plum rectangle, .card-btn-primary a mauve gradient, .pagination-page
   .active a third plum, .filter-btn.active a translucent tint, and
   .btn-secondary-standard was RED - the same colour as a delete action - even
   though it is used for neutral things like "clear filters".

   One family, three intents: primary (act), secondary (neutral), danger
   (destructive). This file loads last, which is what lets a single definition
   replace the scattered per-page ones.
   ============================================================================== */

/*
 * .adm-btn is excluded. This rule matches on the type attribute at (0,3,1),
 * so a submit button carrying .adm-btn-danger came out brand-600 at 12px
 * padding like every other primary button - a Delete that looked like a Save.
 * Same carve-out as :not(.adm-btn) on the link colour above.
 */
.btn-primary-standard,
.card-btn-primary,
.btn-primary,
button[type="submit"]:not(.btn-secondary-standard):not(.btn-danger-standard):not(.adm-btn) {
    background: var(--re-brand-600) !important;
    background-image: none !important;
    border: 1px solid var(--re-border-strong) !important;
    border-radius: var(--re-radius-md) !important;
    color: #ffffff !important;
    font-family: var(--re-font-ui) !important;
    font-size: var(--re-size-ui) !important;
    letter-spacing: var(--re-tracking-ui) !important;
    text-transform: uppercase !important;
    padding: 12px var(--re-space-5) !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    gap: var(--re-space-2) !important;
    cursor: pointer !important;
    text-decoration: none !important;
    transition: background var(--re-transition), transform var(--re-transition),
                box-shadow var(--re-transition) !important;
}

.btn-primary-standard:hover,
.card-btn-primary:hover,
.btn-primary:hover,
button[type="submit"]:not(.btn-secondary-standard):not(.btn-danger-standard):not(.adm-btn):hover {
    background: var(--re-brand-500) !important;
    color: #ffffff !important;
    transform: var(--re-lift) !important;
    box-shadow: var(--re-shadow-1) !important;
}

/* Neutral action. Glass, not red. */
.btn-secondary-standard {
    background: var(--re-surface-sunken) !important;
    border: 1px solid var(--re-border) !important;
    color: var(--re-text-color) !important;
    font-family: var(--re-font-ui) !important;
    font-size: var(--re-size-ui) !important;
    letter-spacing: var(--re-tracking-ui) !important;
    text-transform: uppercase !important;
    border-radius: var(--re-radius-md) !important;
    padding: 12px var(--re-space-5) !important;
}

.btn-secondary-standard:hover {
    background: var(--re-surface-hover) !important;
    border-color: var(--re-border-strong) !important;
    color: var(--re-heading-color) !important;
}

/* ==============================================================================
   COMPONENT LABELS ARE NOT DOCUMENT HEADINGS

   The h1-h6 size scale further up is meant for prose. Small UI labels that
   happen to be marked up as headings were inheriting it, so "PROJECT TYPE" -
   a field label - rendered at 25.6px, the same size as a card title.
   ============================================================================== */

.filter-section-title,
h1.filter-section-title, h2.filter-section-title, h3.filter-section-title,
h4.filter-section-title, h5.filter-section-title, h6.filter-section-title,
.page-subtitle,
.card-badge span,
.copyright-text,
.server-stat-item {
    font-family: var(--re-font-ui) !important;
    font-size: var(--re-size-ui) !important;
    letter-spacing: var(--re-tracking-ui) !important;
    text-transform: uppercase !important;
    font-weight: 400 !important;
}

.filter-section-title {
    color: var(--re-brand-200) !important;
}

/* ==============================================================================
   CHROME BARS

   The breadcrumb, the header controls panel and the two fixed footers are the
   same kind of object - a thin glass rail carrying 8px Silkscreen - and are now
   built from the same tokens so they read as one continuous frame around the
   content rather than four separate widgets.
   ============================================================================== */

.breadcrumb,
.header-controls-panel {
    background: var(--re-surface) !important;
    border: 1px solid var(--re-border) !important;
    border-radius: var(--re-radius-md) !important;
    backdrop-filter: var(--re-glass) !important;
    -webkit-backdrop-filter: var(--re-glass) !important;
    box-shadow: var(--re-shadow-1) !important;
    font-family: var(--re-font-ui) !important;
    font-size: var(--re-size-ui) !important;
}

/* Pagination shares the button family: the active page is a primary button,
   the rest are secondary ones. It was previously the only control still using
   Albert Sans at body size with its own radius. */
.pagination-btn,
.pagination-page {
    font-family: var(--re-font-ui) !important;
    font-size: var(--re-size-ui) !important;
    letter-spacing: var(--re-tracking-ui) !important;
    text-transform: uppercase !important;
    border-radius: var(--re-radius-md) !important;
    background: var(--re-surface-sunken) !important;
    border: 1px solid var(--re-border) !important;
    color: var(--re-text-color) !important;
}

.pagination-btn:hover,
.pagination-page:hover {
    background: var(--re-surface-hover) !important;
    border-color: var(--re-border-strong) !important;
    color: var(--re-heading-color) !important;
}

.pagination-page.active {
    background: var(--re-brand-600) !important;
    border-color: var(--re-border-strong) !important;
    color: #ffffff !important;
}


/* ===== assets/css/home-refined.css ===== */
/**
 * Home page layout, plus one site-wide chrome correction.
 * =======================================================
 *
 * Most of this file is the home page. The exception is the "Page chrome"
 * section below, which applies everywhere: it has to load after
 * glass-standard.css to outweigh an !important there, and this is the last
 * stylesheet the page pulls in. If this file is ever renamed or reordered,
 * that rule has to move with it or the theme strip goes back to being a card
 * on every page.
 *
 * The page used to be a single column of seven identically-sized article
 * blocks, alternating dark and light. Everything had the same weight, so
 * nothing led: a visitor landed on a devlog feed with no idea whose site it
 * was or what the work looked like.
 *
 * This gives it an order to read in - who, then the work, then the writing -
 * and a grid to sit on. It changes no colours, fonts or surfaces: the ramp
 * (--re-brand-*), the three faces (ThaleahFat display, Silkscreen UI, Albert
 * Sans body) and the glass tokens are all the existing ones. What changes is
 * scale, spacing and placement.
 *
 * Type roles, kept consistent throughout:
 *   Silkscreen   small caps labels only - eyebrows, meta, stat keys. A pixel
 *                face reads as craft at 9-11px and as noise at paragraph
 *                size, so it never sets running text here.
 *   ThaleahFat   display headings only.
 *   Albert Sans  everything a person actually reads.
 */

/* ------------------------------------------------------------------ *
 * Visibility
 * ------------------------------------------------------------------ */

/*
 * Nothing on this page may depend on JavaScript to become visible.
 *
 * The first version of this layout carried AOS scroll animations, and on the
 * live page all nine panels stayed at opacity 0 with a 100px offset - the
 * hero included, which had even been given the .aos-animate class and still
 * never resolved. The page was fully built, images loaded, text in place,
 * and completely invisible.
 *
 * The attributes are gone from the markup, but AOS's stylesheet still ships
 * site-wide and matches on [data-aos]. This makes sure that if one is ever
 * pasted back in here - or another library reaches in - the content is shown
 * regardless. An entrance animation is worth nothing next to the risk of a
 * blank homepage.
 */
.home [data-aos] {
    opacity: 1 !important;
    transform: none !important;
    visibility: visible !important;
}

/* ------------------------------------------------------------------ *
 * Page chrome - site wide
 * ------------------------------------------------------------------ */

/*
 * The theme/account strip was a full glass card: filled, bordered, rounded,
 * with a 32px drop shadow. It is a utility bar - theme toggle, account,
 * language - and at that weight it read as the first content on every page,
 * competing with whatever the page was actually about.
 *
 * It now keeps a single hairline and nothing else. This applies to every
 * page, not just the home page: the first pass scoped it to the home page
 * and that left the site inconsistent with itself, the chrome changing
 * shape as you moved between pages.
 *
 * It lives in this stylesheet rather than header-controls-panel.css because
 * glass-standard.css applies the card treatment with !important, and this is
 * the last stylesheet the page loads - the only place a rule is guaranteed
 * to outweigh it.
 */
.header-controls-panel {
    /*
     * !important throughout: glass-standard.css applies the fill, blur and
     * border to this panel with !important, and a mobile block in
     * header-controls-panel.css re-adds the backdrop filter. This file loads
     * after both, so matching their weight is the only way to win.
     */
    background: transparent !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    border: none !important;
    border-bottom: 1px solid rgba(130, 107, 125, 0.18) !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 10px 4px;
    margin-top: 8px;
    margin-bottom: 0;
}

/* The card's noise and sheen overlays go with it. */
.header-controls-panel::before,
.header-controls-panel::after {
    display: none;
}

/* ------------------------------------------------------------------ *
 * Rhythm
 * ------------------------------------------------------------------ */

.home {
    /* One spacing scale for the whole page, so sections relate instead of
       each carrying its own arbitrary margin. */
    --home-max: 1180px;
    --home-section: clamp(56px, 8vw, 112px);
    --home-block: clamp(24px, 3vw, 40px);
    /*
     * The hairline is defined on :root in theme-tokens.css now, so this local
     * copy is redundant - it is kept only so the home page still has a rule
     * colour if that file is ever loaded without this one. Same value.
     */
    --home-rule: color-mix(in srgb, var(--re-brand-400) 22%, transparent);

    max-width: var(--home-max);
    margin: 0 auto;
    padding: 0 24px;
}

.home-section {
    margin-bottom: var(--home-section);
}

.home-section:last-child {
    margin-bottom: 0;
}

/* A section header: pixel eyebrow, display title, hairline. */
.home-eyebrow {
    font-family: var(--re-font-ui);
    font-size: 9px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--re-brand-300);
    opacity: 0.85;
    margin: 0 0 10px;
    display: block;
}

.home-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
    margin-bottom: var(--home-block);
    padding-bottom: 14px;
    border-bottom: 1px solid var(--home-rule);
}

.home-section-title {
    font-family: var(--re-font-display);
    font-size: clamp(1.5rem, 3vw, 2.1rem);
    letter-spacing: 0.02em;
    color: var(--re-text-color);
    margin: 0;
    line-height: 1.1;
}

.home-section-link {
    font-family: var(--re-font-ui);
    font-size: 9px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--re-brand-300);
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.25s ease, transform 0.25s ease;
}

.home-section-link:hover {
    color: var(--re-text-color);
    transform: translateX(3px);
}

/* ------------------------------------------------------------------ *
 * Hero
 * ------------------------------------------------------------------ */

.home-hero {
    padding: clamp(40px, 7vw, 96px) 0 var(--home-section);
    /*
     * Statement left, numbers in the right margin.
     *
     * It was three columns - portrait, text, numbers - and the portrait
     * column was what squeezed the statement into a narrow middle lane. The
     * portrait moved into the byline, so the text column now takes
     * everything the rail does not, and the numbers keep the margin they
     * have always had.
     */
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: clamp(28px, 5vw, 56px);
    align-items: start;
}

/*
 * The portrait: 48x48 pixel art, so it may only ever be drawn at a whole
 * multiple of its own size.
 *
 * 144px is 3x exactly. Anything between the multiples - a percentage, a vw
 * unit, a clamp() - puts source pixels on fractional screen pixels and the
 * browser blends them, which is what turns pixel art to mush. The size is
 * therefore stated in fixed px at each breakpoint and never interpolated.
 *
 * image-rendering: pixelated is what stops the upscale being smoothed. The
 * two lines above it are the older spellings, kept for Firefox and Safari
 * versions that never took the standard keyword.
 */
.home-avatar {
    /* 48px is the source's own size - a 1:1 draw, the one case where the
       question of fractional screen pixels cannot arise at all. It has been
       144 (3x) leading the masthead and 96 (2x) holding the middle of the
       column; at the head of a two-line identity row it sits at 1x, matching
       the height of the name and eyebrow beside it. Only whole multiples are
       ever allowed here: a percentage, a vw unit or a clamp() puts source
       pixels on fractional screen pixels and the browser blends them, which
       is what turns pixel art to mush. */
    width: 48px;
    height: 48px;
    display: block;
    image-rendering: -moz-crisp-edges;
    image-rendering: -webkit-optimize-contrast;
    image-rendering: pixelated;
    /* A drop-shadow rather than a box-shadow: it follows the alpha edge of
       the sprite instead of drawing a rectangle behind it, so the art keeps
       its silhouette against the moving background. Scaled back with the
       sprite - a 14px blur under a 48px figure is a smudge, not a shadow. */
    filter: drop-shadow(0 3px 9px rgba(0, 0, 0, 0.55));
    flex: 0 0 auto;
}

/*
 * The claim, at the size the name used to be.
 *
 * The proposition is what the page is for, so it gets the display face and
 * the largest type; the name is a byline below it. Slightly smaller than the
 * name was, because this is a sentence rather than two words and has to hold
 * a measure - 20ch keeps it to two or three lines at every width.
 */
.home-hero-claim {
    font-family: var(--re-font-display);
    /* !important because theme-color-fix.css sizes h1 at 2.5rem with it, and
       a class beats a bare tag on specificity but not an !important
       declaration without one of its own. Without this the claim was a flat
       40px at every width - the clamp never applied. */
    font-size: clamp(2.2rem, 6.4vw, 4.6rem) !important;
    line-height: 1.02;
    letter-spacing: 0.01em;
    /* Wide enough to break two lines rather than three - at 20ch the
       English claim left "play." stranded on its own line, which reads as a
       dropped sentence rather than a statement. */
    max-width: 22ch;
    margin: 0 0 28px;
    color: var(--re-text-color);
    /* The background art sits directly behind this, so the type needs its
       own weight against a moving field. */
    text-shadow: 0 2px 24px rgba(0, 0, 0, 0.55);
}

/* ------------------------------------------------------------------ *
 * Hardware
 * ------------------------------------------------------------------ */

/*
 * "Has developed on", and the wordmarks of everything the portfolio has
 * shipped on. This replaced three cards reading Console / Arcade / VR with a
 * title under each: the marks say the same thing at a glance, and a logo a
 * studio recognises carries further than the category it belongs to.
 *
 * The label sits above rather than inline, because the row of marks is
 * ragged-width and a label beside it left an uneven gap at every breakpoint.
 */
.home-platforms {
    margin: 0 0 30px;
}

/* No font-size: the pixel grid owns the size of every Silkscreen face. */
.home-platforms-key {
    display: block;
    margin-bottom: 14px;
    font-family: var(--re-font-ui);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--re-brand-300);
    opacity: 0.75;
}

/*
 * The marks themselves. platform-badges.php sizes each one against a common
 * cap height; this only sets the rhythm between them and gives the row room
 * to wrap on a narrow screen.
 */
/*
 * The marquee viewport.
 *
 * The row used to wrap - 8 + 4 at 1440, six lines on a phone. It is one line
 * now and it moves, which fits any number of marks at any width.
 *
 * The mask fades both ends so a wordmark enters and leaves rather than
 * appearing at a hard edge. It is decorative: where mask-image is not
 * supported the row simply has crisp edges, which is what it had before.
 */
.home-marquee {
    overflow: hidden;
    /* The track is one long line; without this the flex parent would let it
       set the hero's width. */
    width: 100%;
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 48px,
                                        #000 calc(100% - 48px), transparent 100%);
            mask-image: linear-gradient(to right, transparent 0, #000 48px,
                                        #000 calc(100% - 48px), transparent 100%);
}

/*
 * The track: N copies of the list side by side, slid left by exactly one
 * copy. --home-marquee-shift is -(100/N)% and comes from the template, which
 * is the only place that knows N.
 */
.home-marquee-track {
    display: flex;
    align-items: center;
    /*
     * gap: 0 here, deliberately - the spacing between copies lives on the
     * copies themselves.
     *
     * With a gap on the track, N copies measure N*copy + (N-1)*gap, so
     * sliding 100/N per cent moves LESS than one copy: measured at 1258px a
     * copy and 26px a gap, the animation travelled 1271px where the next copy
     * began at 1284. Thirteen pixels of stutter on every loop.
     *
     * Each copy carrying its own trailing space makes the track exactly
     * N * copy, and the arithmetic lands.
     */
    gap: 0;
    width: max-content;
    animation: home-marquee-scroll var(--home-marquee-time, 40s) linear infinite;
}

/* The space between one copy and the next, matching the rhythm within a copy
   so the join is not visible as a wider or narrower gap. */
.home-marquee-track > .home-platform-marks {
    padding-right: 26px;
}

@keyframes home-marquee-scroll {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(var(--home-marquee-shift, -50%), 0, 0); }
}

/* Stop it while someone is looking at a particular mark - the badges have
   their own hover state, and it is unusable on a moving target. */
.home-marquee:hover .home-marquee-track,
.home-marquee:focus-within .home-marquee-track {
    animation-play-state: paused;
}

.home-platform-marks {
    display: flex;
    /* nowrap: inside the marquee the row must stay one line. The wrapping
       version is restored under reduced motion below. */
    flex-wrap: nowrap;
    align-items: center;
    gap: 18px 26px;
    margin: 0;
    padding: 0;
    list-style: none;
    flex: 0 0 auto;
}

/*
 * Asked not to animate: don't.
 *
 * The duplicates exist only to make the loop seamless, so they go, and the
 * one real copy wraps again - the exact row this replaced. Nothing is hidden
 * from anyone who turns motion off.
 */
@media (prefers-reduced-motion: reduce) {
    .home-marquee {
        -webkit-mask-image: none;
                mask-image: none;
    }

    .home-marquee-track {
        animation: none;
        width: auto;
        flex-wrap: wrap;
    }

    /* No copies to separate once the duplicates are hidden. */
    .home-marquee-track > .home-platform-marks {
        padding-right: 0;
    }

    .home-marquee-track > .home-platform-marks:not(:first-child) {
        display: none;
    }

    .home-platform-marks {
        flex-wrap: wrap;
    }
}

.home-platform-marks .platform-badge img {
    /* Taller than the in-card marks: this is the hero, and 11px wordmarks
       that read fine in a card are unreadable as a standalone row. */
    height: 20px;
    width: auto;
    opacity: 0.82;
    transition: opacity 0.25s ease;
}

.home-platform-marks .platform-badge:hover img {
    opacity: 1;
}

/* A platform with no wordmark in the library falls back to its name. */
.home-platform-marks .platform-badge-text {
    font-family: var(--re-font-ui);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.7;
}

/* ------------------------------------------------------------------ *
 * Byline
 * ------------------------------------------------------------------ */

/*
 * The identity line: portrait, name, role. Heads the hero column.
 *
 * It was below the proof paragraph, with the role and location up at the top
 * on their own - so the reader met the job title, then the claim, then the
 * evidence, and only then found out whose it was. One line at the top says it
 * once, and the column below it is all argument.
 */
.home-byline {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 0 0 26px;
}

.home-byline-text {
    display: flex;
    flex-direction: column;
    /* Small: the name and the role are one statement on two lines, not two
       stacked items. */
    gap: 6px;
    min-width: 0;
}

.home-byline-name {
    font-family: var(--re-font-display);
    /* Smaller than it was. It led a block of its own at 30px; here it is a
       byline over a 48px portrait and wants to sit under the claim, not
       compete with it. */
    font-size: clamp(1.15rem, 1.9vw, 1.45rem);
    line-height: 1;
    color: var(--re-text-color);
}

/* The eyebrow keeps its own size and tracking - the pixel grid owns those -
   and only loses the margin it carried as a standalone block. */
.home-byline .home-eyebrow {
    margin: 0;
}

.home-hero-aside {
    font-family: var(--re-font-body);
    font-size: 0.98rem;
    line-height: 1.65;
    color: var(--re-text-color);
    opacity: 0.6;
    /* Matches the format row above it, so the block has one right edge
       rather than three ragged ones. */
    max-width: 62ch;
    margin: 0 0 32px;
}

.home-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

.home-btn {
    font-family: var(--re-font-ui);
    font-size: 9px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    padding: 13px 22px;
    border-radius: var(--re-radius-sm);
    text-decoration: none;
    border: 1px solid var(--re-border);
    color: var(--re-text-color);
    background: var(--re-surface);
    backdrop-filter: blur(10px) saturate(1.4);
    -webkit-backdrop-filter: blur(10px) saturate(1.4);
    transition: transform 0.25s ease, border-color 0.25s ease, background 0.25s ease;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.home-btn:hover {
    transform: translateY(-2px);
    border-color: var(--re-brand-300);
    color: var(--re-text-color);
}

.home-btn-primary {
    background: linear-gradient(135deg, var(--re-brand-500) 0%, var(--re-brand-700) 100%);
    border-color: transparent;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
}

/* The stat rail: keys in pixel type, values in the display face. */
.home-stats {
    /* The rail: keys in pixel type, values in the display face. */
    display: grid;
    gap: 22px;
    padding-left: clamp(20px, 3vw, 36px);
    border-left: 1px solid var(--home-rule);
}

.home-stat-value {
    font-family: var(--re-font-display);
    font-size: clamp(1.6rem, 3vw, 2.4rem);
    line-height: 1;
    color: var(--re-text-color);
    display: block;
}

.home-stat-key {
    font-family: var(--re-font-ui);
    font-size: 8px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--re-brand-300);
    opacity: 0.8;
    margin-top: 6px;
    display: block;
}

/* ------------------------------------------------------------------ *
 * Featured work
 * ------------------------------------------------------------------ */

.home-feature {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: clamp(20px, 3vw, 40px);
    align-items: center;
    margin-bottom: var(--home-block);
}

.home-feature-media,
.home-card-media {
    position: relative;
    overflow: hidden;
    border-radius: var(--re-radius-lg);
    border: 1px solid var(--re-border);
    background: var(--re-surface-sunken);
    aspect-ratio: 16 / 10;
}

.home-feature-media img,
.home-card-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.home-feature:hover .home-feature-media img,
.home-card:hover .home-card-media img {
    transform: scale(1.04);
}

/* Nothing uploaded yet: a quiet placeholder rather than a broken frame. */
.home-media-empty {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--re-font-ui);
    font-size: 9px;
    letter-spacing: 0.16em;
    color: var(--re-brand-300);
    opacity: 0.4;
}

.home-feature-title {
    font-family: var(--re-font-display);
    /* !important for the same reason as the card title: h3's 1.6rem in
       theme-color-fix.css was capping this clamp at its own minimum. */
    font-size: clamp(1.6rem, 3.4vw, 2.6rem) !important;
    line-height: 1.05;
    margin: 12px 0 14px;
    color: var(--re-text-color);
}

.home-feature-title a,
.home-card-title a {
    color: inherit;
    text-decoration: none;
}

.home-feature-title a:hover,
.home-card-title a:hover {
    color: var(--re-brand-300);
}

.home-feature-desc {
    font-family: var(--re-font-body);
    font-size: 1rem;
    line-height: 1.65;
    color: var(--re-text-color);
    opacity: 0.75;
    margin: 0 0 20px;
}

/* ------------------------------------------------------------------ *
 * Work grid
 * ------------------------------------------------------------------ */

.home-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: clamp(18px, 2.4vw, 28px);
}

.home-card {
    display: flex;
    flex-direction: column;
    gap: 12px;
    text-decoration: none;
    transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.home-card:hover {
    transform: translateY(-4px);
}

.home-card-title {
    /*
     * The display face, taken from theme-color-fix.css's h1-h6 rule by not
     * overriding it - which also brings font-weight:normal and switches
     * antialiasing off, both of which a pixel face needs.
     *
     * The size has to be !important, and that is not a preference: the same
     * file sets `h3 { font-size: 1.6rem !important }`, which was quietly
     * winning over the 1.02rem this rule used to ask for. Card and feature
     * titles were both landing on 25.6px and only differed by face. Now that
     * they share a face, the sizes have to do the separating.
     */
    font-size: clamp(1.15rem, 1.5vw, 1.35rem) !important;
    line-height: 1.25;
    letter-spacing: 0.01em;
    color: var(--re-text-color);
    margin: 0;
}

.home-card-desc {
    font-family: var(--re-font-body);
    font-size: 0.88rem;
    line-height: 1.55;
    color: var(--re-text-color);
    opacity: 0.62;
    margin: 0;
}

/* Meta line shared by features and cards. */
.home-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    font-family: var(--re-font-ui);
    font-size: 8px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--re-brand-300);
    opacity: 0.9;
}

.home-meta-dot {
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.6;
}

.home-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 4px;
}

.home-tag {
    /*
     * The UI face, like every other tag on the site.
     *
     * This used to be the body face, on the grounds that Silkscreen has no
     * '#' and a tag reading "C#" came out as a missing-character box. That
     * is not true of the font as shipped: rendered at 48px, Silkscreen's '#'
     * draws 492 ink pixels and '+' draws 304, while a genuinely absent glyph
     * (U+FFFF) draws nothing at all. Whatever the original problem was, it
     * was not a missing glyph, and one tag in the body face was the only
     * thing on the page not matching the others.
     */
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    padding: 5px 10px;
    border-radius: var(--re-radius-pill);
    border: 1px solid var(--re-border);
    color: var(--re-text-color);
    opacity: 0.7;
}

/* ------------------------------------------------------------------ *
 * Devlog
 * ------------------------------------------------------------------ */

.home-posts {
    display: grid;
    gap: 2px;
}

/*
 * Rows, not cards. Seven glass panels stacked down the page was the old
 * layout's main problem: writing is secondary to the work here, so it gets
 * a dense readable list and the work keeps the visual weight.
 */
.home-post {
    display: grid;
    grid-template-columns: 92px minmax(0, 1fr) auto;
    gap: clamp(16px, 2.5vw, 32px);
    align-items: baseline;
    padding: 22px 16px;
    border-bottom: 1px solid var(--home-rule);
    text-decoration: none;
    border-radius: var(--re-radius-sm);
    transition: background 0.25s ease, padding-left 0.25s ease;
}

.home-post:hover {
    background: rgba(130, 107, 125, 0.08);
    padding-left: 24px;
}

.home-post-date {
    font-family: var(--re-font-ui);
    font-size: 8px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-brand-300);
    opacity: 0.8;
    white-space: nowrap;
}

.home-post-title {
    /*
     * The display face, deliberately - and by NOT overriding it.
     *
     * theme-color-fix.css puts ThaleahFat on every h1-h6 with !important, so
     * this rule only has to stay out of the way. That matters beyond the font
     * name: the same declaration turns font-weight down to normal and switches
     * antialiasing off, and a pixel face needs both. Setting font-family here
     * instead would take the face and leave the smearing.
     *
     * Larger than the body-face version it replaces. ThaleahFat carries much
     * less ink per em, so matching the old 1.05rem by the number would have
     * read a size smaller than the excerpt beneath it.
     */
    font-size: clamp(1.15rem, 1.7vw, 1.4rem);
    line-height: 1.2;
    letter-spacing: 0.01em;
    color: var(--re-text-color);
    margin: 0 0 8px;
}

.home-post-excerpt {
    font-family: var(--re-font-body);
    font-size: 0.88rem;
    line-height: 1.55;
    color: var(--re-text-color);
    opacity: 0.6;
    margin: 0;
}

.home-post-cat {
    font-family: var(--re-font-ui);
    font-size: 8px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-brand-300);
    opacity: 0.7;
    white-space: nowrap;
}

/* ------------------------------------------------------------------ *
 * Closing call to action
 * ------------------------------------------------------------------ */

.home-cta {
    text-align: center;
    padding: clamp(40px, 6vw, 72px) 24px;
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-lg);
    background: var(--re-surface);
    backdrop-filter: blur(12px) saturate(1.5);
    -webkit-backdrop-filter: blur(12px) saturate(1.5);
}

.home-cta-title {
    font-family: var(--re-font-display);
    font-size: clamp(1.5rem, 3.4vw, 2.4rem);
    margin: 0 0 12px;
    color: var(--re-text-color);
}

.home-cta-text {
    font-family: var(--re-font-body);
    font-size: 1rem;
    line-height: 1.6;
    color: var(--re-text-color);
    opacity: 0.7;
    margin: 0 auto 26px;
    max-width: 48ch;
}

.home-cta .home-actions {
    justify-content: center;
}

/* ------------------------------------------------------------------ *
 * Narrow screens
 * ------------------------------------------------------------------ */

@media (max-width: 900px) {
    .home-feature {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 768px) {
    .home {
        padding: 0 16px;
    }

    /* One column. At 375px there is no room for a portrait beside a name set
       at 9vw, and the two-column version centred the sprite against a 700px
       block, stranding it level with the body copy instead of the name.
       Stacked, the order reads portrait, name, copy, numbers. */
    .home-hero {
        grid-template-columns: minmax(0, 1fr);
        gap: 20px;
    }

    /* 48px is 1x - still a whole multiple, so still crisp. */
    .home-avatar {
        width: 48px;
        height: 48px;
    }

    /* Stacked at this width, so the rail becomes a row under the copy. */
    .home-stats {
        grid-auto-flow: column;
        justify-content: start;
        gap: 28px;
        padding-left: 0;
        padding-top: 20px;
        border-left: none;
        border-top: 1px solid var(--home-rule);
    }

    /* Date above the title rather than a column that steals half the width. */
    .home-post {
        grid-template-columns: minmax(0, 1fr);
        gap: 8px;
        padding: 18px 12px;
    }

    .home-post:hover {
        padding-left: 16px;
    }

    .home-post-cat {
        justify-self: start;
    }
}

@media (prefers-reduced-motion: reduce) {
    .home-card,
    .home-post,
    .home-btn,
    .home-feature-media img,
    .home-card-media img {
        transition: none;
    }
}

/* ------------------------------------------------------------------ *
 * Platform marks
 * ------------------------------------------------------------------ */

/*
 * The consoles a piece shipped on, drawn as their own wordmarks. The base
 * styling lives in page-refined.css; these two rules only size them for the
 * places they appear on this page.
 *
 * Sized by height and never by width - the marks range from 2.4:1 (Xbox
 * Series) to 7:1 (Nintendo DS), so a common width would render one tiny and
 * another enormous. A shared height is what makes a mixed row read as one set.
 *
 * Both sit under a hairline with clear space above and below. On the portfolio
 * cards these marks originally butted straight against the buttons beneath
 * them, and 12px white logos with nothing between them and a control read as
 * overlapping it.
 */

/*
 * Written as .platform-badges.home-* rather than .home-* alone, and that is
 * not decoration.
 *
 * The base rule lives in page-refined.css:
 *
 *     .platform-badges { margin: 14px 0 0; padding: 0; }
 *
 * and page-refined.css loads AFTER this file. As single-class selectors the
 * two weigh the same, so the later one won: the padding-top set here resolved
 * to 0 and the wordmarks ended up 1px under their own separator, hard against
 * the line.
 *
 * Compounding the classes makes these (0,2,0) against the base's (0,1,0), so
 * they win on specificity and load order stops mattering. Reordering the
 * stylesheets would also have fixed it, and broken something else - that order
 * is what lets page-refined.css correct the pages it is responsible for.
 */

.platform-badges.home-card-platforms {
    margin-top: 12px;
    padding-top: 14px;
    border-top: 1px solid var(--home-rule);
    gap: 7px 12px;
}

.platform-badges.home-card-platforms .platform-badge img {
    height: 11px;
    max-width: 84px;
    opacity: 0.6;
}

.home-card:hover .platform-badges.home-card-platforms .platform-badge img {
    opacity: 0.85;
}

/* The one feature card gets room to breathe. */
.platform-badges.home-feature-platforms {
    margin-top: 20px;
    padding-top: 18px;
    border-top: 1px solid var(--home-rule);
    gap: 10px 20px;
}

.platform-badges.home-feature-platforms .platform-badge img {
    height: 16px;
    max-width: 120px;
    opacity: 0.72;
}

.platform-badges.home-feature-platforms .platform-badge img:hover {
    opacity: 1;
}

@media (max-width: 768px) {
    .platform-badges.home-feature-platforms .platform-badge img {
        height: 14px;
        max-width: 104px;
    }
}


/* ===== assets/css/site-chrome.css ===== */
/**
 * Site chrome: header controls, footer and music player.
 * ======================================================
 *
 * The footer and the player are owned outright by this file. The header
 * controls strip is not - header-controls-panel.css still owns it, and the
 * short section at the end of this file only corrects its narrow-screen
 * layout.
 *
 * It replaces these five, whose links are removed from templates/header.php:
 *
 *   music-player-v2.css          music-player-unified-fix.css
 *   footer-layout.css            footer-gap-fix.css
 *   footer-copyright-fix.css
 *
 * That is the point of it. Those files set the same properties on the same
 * selectors at different values, so every rule in the later ones needed
 * !important to land, and a sixth file would have needed !important to beat
 * those. Almost nothing here carries !important, because nothing here is
 * arguing with anything.
 *
 *
 * WHAT CHANGED, AND WHY
 *
 * The footer was not a footer. .site-footer was position:fixed at bottom:40px
 * and the server strip fixed at bottom:0, so the bottom 80px of every
 * viewport was permanently spent - on a phone that is a tenth of the screen,
 * held by a copyright line - and body carried padding-bottom:110px to keep
 * content out from under it. The player then had to float above both at
 * bottom:120px, which is why a 500x440 card sat in the middle of the screen,
 * covering the hero on mobile and the devlog on desktop.
 *
 * So: the footer returns to normal document flow and arrives when the page
 * ends, and the player - the one thing that genuinely has to survive
 * navigation - is the only fixed element left. It is docked to the bottom
 * edge as a bar rather than parked in the middle as a card.
 *
 * Type and colour are the home page's: the --re-brand ramp, --home-rule for
 * hairlines, Silkscreen for small caps labels only, Albert Sans for anything
 * read as a sentence.
 */

/* ------------------------------------------------------------------ *
 * Page model
 * ------------------------------------------------------------------ */

/*
 * Only the player bar is fixed now, so this is the only reserved space at the
 * bottom of the page. The class is added by music-player.js once the bar is
 * actually in the DOM - if that script fails to load, the page simply has no
 * dead strip at the bottom.
 */
body.has-player-bar {
    /* 13 + 45 + 15 + 1px border = 74. Rounded up so the last line of a page
       is never touching the bar. */
    padding-bottom: 80px;
}

/* ------------------------------------------------------------------ *
 * Footer
 * ------------------------------------------------------------------ */

/*
 * The !important here is not a fight with the files this one replaced - it is
 * glass-standard.css, which paints every <footer> element as a glass card:
 *
 *   footer, .footer { background: var(--re-surface) !important;
 *                     backdrop-filter: var(--glass-blur) !important;
 *                     border-top: 1px solid var(--re-border) !important;
 *                     box-shadow: 0 -8px 32px rgba(0,0,0,.5),
 *                                 inset 0 1px 0 rgba(255,255,255,.18)
 *                                 !important; }
 *
 * That is the right treatment for a bar hovering over content, which is what
 * the footer used to be. In normal flow, running the full width at the end of
 * the page, a translucent panel with a lit top edge and a shadow reads as a
 * card floating on top of itself. The page's own ground shows through
 * instead, and the only division is the hairline - the same one under the
 * theme strip and between home page sections.
 *
 * home-refined.css carries the identical override for the controls panel and
 * explains the same constraint: this file loads after glass-standard, so
 * matching !important is what it takes. Nothing else in here needs it.
 */
/* ==================================================================== *
 * ONE PAGE COLUMN
 *
 * The footer, the home column and every section rule stop at --home-max
 * (1180px). The nav, the theme/region controls and the page content well
 * (#re-main) did not - they used Bootstrap's .container, which is 1320px on a
 * desktop - so the chrome overhung the content and the footer by ~70px a side.
 *
 * They share the one column now, and at the one side inset (24px) the content
 * is read at: #re-main's inline padding:0 is overridden, and .home drops its
 * own copy of that inset so the two do not stack. Brand, controls, content,
 * footer and every hairline now begin and end on the same pair of edges.
 *
 * This file loads after Bootstrap, home-refined and header-controls-panel, so
 * matching !important is what it takes to win.
 * ==================================================================== */
.navbar > .container,
.header-controls-panel,
#re-main {
    max-width: var(--home-max, 1180px) !important;
    margin-left: auto !important;
    margin-right: auto !important;
}

.navbar > .container,
#re-main {
    padding-left: 24px !important;
    padding-right: 24px !important;
}

.home {
    padding-left: 0 !important;
    padding-right: 0 !important;
}

.site-footer {
    position: static;
    margin-top: var(--home-section, 96px);
    background: transparent !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    /*
     * The rule across the top of the footer belongs to .footer-inner, not to
     * this element.
     *
     * As a border here it ran the full width of the window, while every other
     * rule on the site stops at --home-max - including the one directly below
     * it, above the base row, which is 1180px against this one's 1430px on a
     * desktop. Two hairlines a few hundred pixels apart, at different widths,
     * and the mismatch is the only thing you see.
     */
    border-top: 0 !important;
    box-shadow: none !important;
    border-radius: 0 !important;
}

/* glass-standard's inset highlight along the top edge. */
.site-footer::before,
.site-footer::after {
    display: none !important;
}

.footer-inner {
    max-width: var(--home-max, 1180px);
    margin: 0 auto;
    padding: 48px 24px 28px;
    /* Same box, same declaration, as the rule above .footer-base below - so
       the footer's two hairlines line up instead of one overshooting. */
    border-top: 1px solid var(--home-rule, rgba(130, 107, 125, 0.22));
    display: grid;
    /* The identity column is wider because it carries a portrait and two
       lines; the other two are lists of short links. */
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr);
    gap: 40px;
    align-items: start;
}

/* --- identity ----------------------------------------------------- */

.footer-identity {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 16px;
    align-items: center;
}

/*
 * 48px is 1x - the sprite's own size, so no resampling happens at all and
 * image-rendering is academic here. It is set anyway so that changing this
 * number to 96 later still lands on a whole multiple and still stays crisp.
 */
.footer-avatar {
    width: 48px;
    height: 48px;
    display: block;
    image-rendering: -moz-crisp-edges;
    image-rendering: -webkit-optimize-contrast;
    image-rendering: pixelated;
}

.footer-name {
    display: block;
    font-family: var(--re-font-display);
    font-size: 1.15rem;
    line-height: 1.2;
    color: var(--re-text-color);
}

.footer-role,
.footer-eyebrow {
    display: block;
    font-family: var(--re-font-ui);
    font-size: 0.62rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
}

.footer-role {
    margin-top: 6px;
}

.footer-eyebrow {
    margin-bottom: 14px;
}

/* --- link columns ------------------------------------------------- */

.footer-column {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

.footer-links {
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.footer-links a,
.footer-legal a {
    line-height: 1.3;
    color: var(--re-text-color);
    opacity: 0.62;
    text-decoration: none;
    width: fit-content;
    transition: opacity 0.18s ease, transform 0.18s ease;
}

/*
 * The site column takes the UI face, matching the SITE eyebrow directly above
 * it. The headings were already Silkscreen and their contents were not, which
 * read as two footers stacked on each other.
 *
 * 0.68rem rather than the 0.88rem the body face used. Silkscreen carries far
 * more width and height per character, and at the old number these links came
 * out larger than the heading they sit under.
 *
 * The typography moved out of the shared rule above rather than being
 * overridden here, because .footer-legal a - the bottom bar - is grouped with
 * it and sets its own size. Two rules quietly disagreeing about one selector
 * is what made the legal links need their own correction in the first place.
 */
.footer-links a {
    font-family: var(--re-font-ui);
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.footer-links a:hover,
.footer-legal a:hover {
    opacity: 1;
    color: var(--re-text-color);
    transform: translateX(3px);
}

/* --- status ------------------------------------------------------- */

.footer-status {
    display: flex;
    align-items: center;
    gap: 9px;
    /* Same face and size as the site links opposite it - the two columns sit
       side by side and should not read as different kinds of text. */
    font-family: var(--re-font-ui);
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.85;
    margin-bottom: 12px;
}

/*
 * The dot pulses rather than sits still, because "open to work" is a claim
 * about right now. Reduced-motion callers get a static dot, at the end of
 * this file.
 */
.footer-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #4ec98a;
    box-shadow: 0 0 0 0 rgba(78, 201, 138, 0.55);
    animation: footerPulse 2.4s ease-out infinite;
    flex: 0 0 auto;
}

@keyframes footerPulse {
    0%   { box-shadow: 0 0 0 0 rgba(78, 201, 138, 0.5); }
    70%  { box-shadow: 0 0 0 8px rgba(78, 201, 138, 0); }
    100% { box-shadow: 0 0 0 0 rgba(78, 201, 138, 0); }
}

.footer-cta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-family: var(--re-font-ui);
    font-size: 0.66rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--re-text-color);
    padding: 9px 16px;
    border: 1px solid var(--re-brand-400);
    border-radius: var(--re-radius-sm, 6px);
    transition: background 0.18s ease, border-color 0.18s ease;
}

.footer-cta:hover {
    background: rgba(var(--re-brand-400-rgb), 0.16);
    border-color: var(--re-brand-200);
    color: var(--re-text-color);
}

/* --- base row ----------------------------------------------------- */

.footer-base {
    max-width: var(--home-max, 1180px);
    margin: 0 auto;
    padding: 18px 24px 26px;
    border-top: 1px solid var(--home-rule, rgba(130, 107, 125, 0.22));
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
}

.footer-copy {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--re-font-ui);
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.45;
}

.footer-legal {
    display: flex;
    align-items: center;
    gap: 22px;
    margin-left: auto;
}

/*
 * The bottom bar is Silkscreen: the copyright to the left of these links and
 * the "Back to top" button to their right are both var(--re-font-ui) at
 * 0.6rem, uppercase. These two were the only things on the line still in
 * Albert Sans at 0.8rem, sentence case - the face rule above is written for
 * the FOOTER COLUMNS (it is grouped with .footer-links), and the bottom-bar
 * block only ever corrected the size, so the body face came through.
 */
.footer-legal a {
    font-family: var(--re-font-ui);
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    opacity: 0.45;
}

/*
 * And they fade rather than slide. translateX on hover reads correctly in the
 * vertical column of site links; on a horizontal bar it nudges the link into
 * the gap beside "Back to top", which does not move.
 */
.footer-legal a:hover {
    opacity: 1;
    transform: none;
}

.footer-top {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: none;
    border: none;
    padding: 6px 2px;
    cursor: pointer;
    font-family: var(--re-font-ui);
    font-size: 0.6rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    transition: opacity 0.18s ease;
}

.footer-top:hover {
    opacity: 1;
}

/* --- server strip (admins only; gated in templates/footer.php) ----- */

.server-info-footer {
    position: static;
    max-width: var(--home-max, 1180px);
    margin: 0 auto;
    padding: 10px 24px 22px;
    opacity: 0.55;
}

.server-stats-grid {
    display: flex;
    flex-wrap: wrap;
    /* Spread the stats across the full column so they span the hairline above
       them (the .footer-base / .footer-inner rule at --home-max) rather than
       bunching at the left. */
    justify-content: space-between;
    gap: 8px 22px;
}

/*
 * These rules used to live in a <style> block inside templates/footer.php,
 * every declaration !important, because the strip was fighting the same five
 * stylesheets as everything else down here. Nothing competes for these
 * selectors now.
 */
.server-stat-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
    font-family: var(--re-font-ui);
    font-size: 8px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    /* Silkscreen is a bitmap face; smoothing it at 8px turns the stems grey. */
    -webkit-font-smoothing: none;
    text-rendering: optimizeSpeed;
}

.server-stat-item i {
    font-size: 11px;
    color: var(--re-brand-400);
}

.server-stat-key {
    color: var(--re-text-color);
    opacity: 0.55;
}

.server-stat-value {
    color: var(--re-text-color);
}

/* ------------------------------------------------------------------ *
 * Music player - docked bar
 * ------------------------------------------------------------------ */

.persistent-player {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* The old rule centred with translateX(-50%) against left:50%. Edge to
       edge needs neither, and a stale transform would drag the bar off
       screen. */
    transform: none;
    z-index: 9990;
    /*
     * The player's own ground, from the palette rather than a fixed plum.
     *
     * This was rgba(26, 20, 30, 0.82) - a dark mauve baked in when mauve was
     * the only palette there was. Once the site could turn green or teal or
     * grey, the bar across the bottom stayed purple and stopped belonging to
     * the page above it.
     *
     * brand-900 is the palette's darkest step, which is what the ground
     * gradient is built from too, so the two now agree by construction. The
     * rgba line stays as the fallback for browsers without color-mix; the
     * second declaration wins everywhere else and, because brand-900 is a
     * registered property, it crossfades with everything else.
     */
    background: rgba(26, 20, 30, 0.82);
    background: color-mix(in srgb, var(--re-brand-900) 82%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(160%);
    backdrop-filter: blur(18px) saturate(160%);
    border-top: 1px solid var(--home-rule, rgba(130, 107, 125, 0.22));
    /* Upward, because the bar is lit from the page above it. */
    box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.38);
}

/*
 * The bar's breathing room is PADDING, not slack.
 *
 * This used to be `padding: 0 24px` with `min-height: 64px`, and it looked
 * fine closed for one reason only: the content came to about 50px, so
 * align-items:center shared the leftover 14px above and below it. That is not
 * spacing, it is a leftover, and it disappears the moment there is no leftover
 * to share. Open the queue and the content becomes 458px - taller than the
 * min-height - so the centring had nothing to distribute and everything went
 * flush to the edges: the album art of the playing track sat ONE pixel below
 * the top border, with the transport row jammed against it.
 *
 * Stated padding holds in both states, so the bar is spaced the same whether
 * the queue is open or shut.
 */
.player-container {
    max-width: var(--home-max, 1180px);
    margin: 0 auto;
    padding: 13px 24px 15px;
    display: flex;
    /*
     * Top, not centre. With the queue open this box is ~450px tall, and
     * centring stretched the space above the transport row down the whole
     * panel. The minimize button no longer depends on this - it is a grid
     * item on the transport row itself; see .player-toggle.
     */
    align-items: flex-start;
    gap: 16px;
}

/*
 * The markup order is fixed by getPlayerHTML() in music-player.js: track,
 * progress, controls, volume, queue, list. Grid areas re-order it for the eye
 * without the JS having to know anything about the layout.
 */
.player-content {
    flex: 1 1 auto;
    min-width: 0;
    display: grid;
    grid-template-columns: minmax(150px, 260px) auto minmax(140px, 1fr) auto auto auto;
    grid-template-areas:
        "track controls progress volume queue toggle"
        "list  list     list     list   list  list";
    align-items: center;
    gap: 0 20px;
}

.track-info {
    grid-area: track;
    display: flex;
    align-items: center;
    gap: 11px;
    min-width: 0;
}

.player-controls {
    grid-area: controls;
    display: flex;
    align-items: center;
    gap: 4px;
}

.progress-container {
    grid-area: progress;
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.volume-control {
    grid-area: volume;
    display: flex;
    align-items: center;
    gap: 8px;
}

.playlist-header    { grid-area: queue; }
.playlist-container { grid-area: list; }

/* --- track --------------------------------------------------------- */

.track-cover {
    width: 40px;
    height: 40px;
    flex: 0 0 auto;
    border-radius: var(--re-radius-xs, 4px);
    overflow: hidden;
    background: rgba(var(--re-brand-400-rgb), 0.18);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--re-brand-100);
    font-size: 15px;
}

.track-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.track-details {
    min-width: 0;
}

/*
 * Silkscreen at a legible size, and one line only. Track titles run long -
 * "Parasite Eve - Somnia Memorias" - and a bar that grows a second line when
 * the track changes makes the whole page shift under the reader.
 */
.track-title {
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--re-text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/*
 * The artist takes the UI face like the title above it. It was the one line
 * of body text left in the player's own chrome, sitting directly under an
 * uppercase Silkscreen title, which read as an accident rather than a
 * contrast. Uppercase and tracked for the same reason: that is how this face
 * is set everywhere else on the site, and 8px Silkscreen lowercase is the
 * least legible thing it can be asked to do.
 *
 * No size here - silkscreen-pixel-grid.css settles every Silkscreen run at
 * 8px, so a number in this rule would only be a lie about what renders.
 */
.track-artist {
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-top: 3px;
}

/* --- transport ----------------------------------------------------- */

.control-btn {
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    border-radius: 50%;
    padding: 0;
    cursor: pointer;
    color: var(--re-text-color);
    opacity: 0.72;
    font-size: 15px;
    transition: opacity 0.16s ease, background 0.16s ease;
}

.control-btn:hover  { opacity: 1; background: rgba(var(--re-brand-400-rgb), 0.18); }
.control-btn:active { transform: scale(0.94); }
.control-btn.active { opacity: 1; color: var(--re-brand-200); }

.control-btn-play {
    width: 40px;
    height: 40px;
    font-size: 19px;
    opacity: 1;
    background: rgba(var(--re-brand-400-rgb), 0.2);
}

.control-btn-play:hover {
    background: rgba(var(--re-brand-400-rgb), 0.34);
}

/* --- progress ------------------------------------------------------ */

.time-current,
.time-total {
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    letter-spacing: 0.06em;
    color: var(--re-text-color);
    opacity: 0.45;
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
}

.progress-bar {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    height: 4px;
    border-radius: 2px;
    background: rgba(var(--re-brand-400-rgb), 0.28);
    cursor: pointer;
}

/* A larger invisible target than the 4px line, so the bar is grabbable. */
.progress-bar::before {
    content: '';
    position: absolute;
    inset: -9px 0;
}

.progress-fill {
    height: 100%;
    width: 0;
    border-radius: 2px;
    background: var(--re-brand-200);
}

.progress-handle {
    position: absolute;
    top: 50%;
    left: 0;
    width: 11px;
    height: 11px;
    margin-left: -5.5px;
    border-radius: 50%;
    background: var(--re-brand-100);
    transform: translateY(-50%) scale(0);
    transition: transform 0.16s ease;
}

.progress-bar:hover .progress-handle,
.progress-handle:active {
    transform: translateY(-50%) scale(1);
}

/* --- volume -------------------------------------------------------- */

/*
 * Built to read as the same control as .progress-bar above: a 4px rail, the
 * played/filled part in brand-200, and an 11px round handle in brand-100.
 *
 * It did not look like that before, and the reason is worth writing down. The
 * rail was set as the INPUT's own background. With -webkit-appearance: none a
 * range input's box is as tall as its thumb - 11px here, not the 4px the rule
 * asked for - so the browser painted an 84x11 slab with a 2px corner radius.
 * Hence the rectangle. The rail has to live on the track pseudo-element, with
 * the input itself transparent.
 */

.volume-slider {
    -webkit-appearance: none;
    appearance: none;
    width: 84px;
    /* Room for the thumb. The visible rail is drawn by the track below. */
    height: 11px;
    background: none;
    outline: none;
    cursor: pointer;

    /*
     * How much of the rail is filled. Kept in step with the audio by
     * paintVolume() in music-player.js; this value is only the fallback for
     * the instant between markup and the first paint, and matches the
     * value="70" the input is created with.
     */
    --volume-pct: 70%;
}

/*
 * Chrome, Edge, Safari. There is no ::-webkit-range-progress, so the filled
 * part is a hard-stopped gradient rather than a real element.
 */
.volume-slider::-webkit-slider-runnable-track {
    height: 4px;
    border-radius: 2px;
    background: linear-gradient(
        to right,
        var(--re-brand-200) 0%,
        var(--re-brand-200) var(--volume-pct),
        rgba(var(--re-brand-400-rgb), 0.28) var(--volume-pct),
        rgba(var(--re-brand-400-rgb), 0.28) 100%
    );
}

.volume-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    border: none;
    background: var(--re-brand-100);
    cursor: pointer;
    /* (4px rail - 11px thumb) / 2, to sit the handle on the line rather than
       hanging off the top of it. */
    margin-top: -3.5px;
}

/* Firefox draws the filled portion itself, so it needs no gradient. */
.volume-slider::-moz-range-track {
    height: 4px;
    border-radius: 2px;
    background: rgba(var(--re-brand-400-rgb), 0.28);
}

.volume-slider::-moz-range-progress {
    height: 4px;
    border-radius: 2px;
    background: var(--re-brand-200);
}

.volume-slider::-moz-range-thumb {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    border: none;
    background: var(--re-brand-100);
    cursor: pointer;
}

.volume-slider:focus-visible {
    outline: 2px solid var(--re-brand-200);
    outline-offset: 4px;
}

/* --- queue --------------------------------------------------------- */

.playlist-toggle-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: none;
    border: none;
    padding: 8px 4px;
    cursor: pointer;
    font-family: var(--re-font-ui);
    font-size: 0.58rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.6;
    white-space: nowrap;
    transition: opacity 0.16s ease;
}

.playlist-toggle-btn:hover {
    opacity: 1;
}

/*
 * Closed by default and opened by .open, which music-player.js toggles.
 * max-height rather than display, so it can animate and so the chevron has
 * something to point at.
 */
.playlist-container {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.28s ease;
}

/*
 * The gap belongs to the OPEN state, not to the grid.
 *
 * A row-gap on .player-content would also apply while the queue is collapsed -
 * grid still reserves the gap around a zero-height row - and the closed bar
 * would carry a strip of dead space under the transport line. Four pixels was
 * not enough to separate a track list from the track playing above it; the
 * two read as one run of rows.
 */
.playlist-container.open {
    max-height: 46vh;
    overflow-y: auto;
    border-top: 1px solid var(--home-rule, rgba(130, 107, 125, 0.22));
    margin-top: 14px;
    padding-top: 4px;
}

.playlist {
    /* The container's own bottom padding carries the rest of the room under
       the last row, so this only has to clear the rule above the first. */
    padding: 10px 0 4px;
}

.playlist-item {
    display: grid;
    /* cover, title/artist, the playing marker, the remove button.
       Four, not three: the fourth child of a three-column grid is placed on a
       row of its own, which is how a stray X came to sit underneath the track
       that was playing. */
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    align-items: center;
    gap: 12px;
    padding: 9px 12px;
    border-radius: var(--re-radius-xs, 4px);
    cursor: pointer;
    transition: background 0.16s ease;
}

.playlist-item:hover  { background: rgba(var(--re-brand-400-rgb), 0.14); }
.playlist-item.active { background: rgba(var(--re-brand-400-rgb), 0.22); }

.playlist-item-cover {
    width: 30px;
    height: 30px;
    border-radius: var(--re-radius-xs, 4px);
    overflow: hidden;
    background: rgba(var(--re-brand-400-rgb), 0.18);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--re-brand-100);
    font-size: 12px;
}

.playlist-item-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.playlist-item-info {
    min-width: 0;
}

/* The queue reads as chrome, not prose - same face as the player above it. */
.playlist-item-title {
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--re-text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.playlist-item-artist {
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.playlist-item-playing {
    color: var(--re-brand-200);
    font-size: 14px;
}

/*
 * Take a track out of the queue.
 *
 * These styles live here rather than in music-player-v2.css, where they were
 * written first and did nothing: that file was superseded by this one and its
 * <link> taken out of templates/header.php, so the rules were never served to
 * the site. The button fell back to the browser's own - a grey box with a 2px
 * outset border - which is what "X's that do not fit the theme" was.
 *
 * Quiet until the row is hovered, for the reason the admin icons on the music
 * page are: a column of bright crosses reads as a list of things to delete
 * rather than a list of music.
 */
.playlist-item-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    flex-shrink: 0;
    border: 1px solid transparent;
    border-radius: var(--re-radius-xs, 4px);
    background: none;
    color: var(--re-text-color);
    font-size: 10px;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.16s ease, background 0.16s ease,
                border-color 0.16s ease, color 0.16s ease;
}

.playlist-item:hover .playlist-item-remove,
.playlist-item-remove:focus-visible {
    opacity: 0.65;
}

.playlist-item-remove:hover {
    opacity: 1;
    color: #ffb4a8;
    background: rgba(224, 106, 90, 0.18);
    border-color: rgba(224, 106, 90, 0.5);
}

/* Touch has no hover, and the queue is pruned on a phone more than anywhere. */
@media (hover: none) {
    .playlist-item-remove { opacity: 0.5; }
}

/*
 * "This list is yours, and it is not following the default any more."
 *
 * Shown only on a queue that has been edited. Without it, a queue that has
 * stopped tracking the featured set - and the order set for it on the admin
 * page - looks identical to one that is still following, and the difference
 * only shows up as an ordering that appears to be ignored.
 */
.playlist-note {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin: 0 2px 8px;
    padding: 7px 10px;
    border: 1px solid rgba(var(--re-brand-400-rgb), 0.28);
    border-radius: var(--re-radius-xs, 4px);
    background: rgba(var(--re-brand-400-rgb), 0.10);
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.8;
}

.playlist-reset {
    flex: 0 0 auto;
    padding: 5px 10px;
    border: 1px solid rgba(var(--re-brand-400-rgb), 0.45);
    border-radius: var(--re-radius-xs, 4px);
    background: none;
    color: var(--re-brand-100);
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    cursor: pointer;
    transition: background 0.16s ease, border-color 0.16s ease;
}

.playlist-reset:hover {
    background: rgba(var(--re-brand-400-rgb), 0.26);
    border-color: var(--re-brand-200);
}

/* An empty queue is somewhere you can now end up, so it says so. */
.playlist-empty {
    margin: 0;
    padding: 18px 12px;
    text-align: center;
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    line-height: 1.8;
    color: var(--re-text-color);
    opacity: 0.55;
}

.playlist-empty a {
    color: var(--re-brand-200);
    text-decoration: underline;
}

/* --- minimize ------------------------------------------------------ */

.player-toggle {
    /*
     * A grid item on the transport row, so .player-content's align-items
     * centres it against the play button automatically.
     *
     * It used to be a flex sibling of the grid, positioned by hand: first
     * centred against the whole container - which put it halfway down the
     * open queue - and then pushed down by a fixed 7px, which was the right
     * offset for the 45px expanded row and 6px too much for the 32px
     * minimized one. There is no number here to get wrong now.
     */
    grid-area: toggle;
    width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    border-radius: 50%;
    padding: 0;
    cursor: pointer;
    color: var(--re-text-color);
    opacity: 0.5;
    font-size: 14px;
    transition: opacity 0.16s ease, background 0.16s ease;
}

.player-toggle:hover {
    opacity: 1;
    background: rgba(var(--re-brand-400-rgb), 0.18);
}

/*
 * Minimized used to mean an 80px round puck floating in the middle of the
 * screen. Docked, it can just be a shorter bar: cover, title, play. Nothing
 * is hidden that you would reach for while a track is running, and the page
 * behind it gains 20px rather than losing a card.
 */
/*
 * Minimized sets its own padding rather than inheriting the full bar's. With
 * a 32px play button the container above would come to 60px, which is not
 * "minimized" by much.
 */
.persistent-player.minimized .player-container {
    padding: 8px 24px;
}

.persistent-player.minimized .progress-container,
.persistent-player.minimized .volume-control,
.persistent-player.minimized .playlist-header,
.persistent-player.minimized .playlist-container,
.persistent-player.minimized .control-btn:not(.control-btn-play) {
    display: none;
}

.persistent-player.minimized .player-content {
    grid-template-columns: minmax(0, 1fr) auto auto;
    grid-template-areas: "track controls toggle";
}

.persistent-player.minimized .track-cover  { width: 30px; height: 30px; font-size: 12px; }
.persistent-player.minimized .track-artist { display: none; }
.persistent-player.minimized .control-btn-play { width: 32px; height: 32px; font-size: 16px; }

/* ------------------------------------------------------------------ *
 * Narrow screens
 * ------------------------------------------------------------------ */

@media (max-width: 900px) {
    .footer-inner {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: 32px;
    }

    .footer-identity {
        grid-column: 1 / -1;
    }
}

@media (max-width: 768px) {
    body.has-player-bar {
        /* Two rows on a phone - transport, then the progress line - so the
           bar is taller here than on a desktop, not shorter. Measured at 81px;
           the rest is clearance so the last line of a page is not sitting on
           the bar. */
        padding-bottom: 92px;
    }

    .footer-inner { padding: 36px 16px 24px; }
    .footer-base  { padding: 16px 16px 22px; }

    .footer-legal {
        margin-left: 0;
        gap: 18px;
    }

    .player-container {
        padding: 10px 14px 12px;
        gap: 10px;
    }

    /*
     * Two rows on a phone: identity and transport on top, the progress line
     * beneath them running the full width. Volume goes - there is a hardware
     * rocker for that - and the queue label collapses to its icon.
     */
    .player-content {
        grid-template-columns: minmax(0, 1fr) auto auto auto;
        grid-template-areas:
            "track    controls queue    toggle"
            "progress progress progress progress"
            "list     list     list     list";
        gap: 4px 10px;
    }

    .volume-control {
        display: none;
    }

    .playlist-toggle-btn span {
        display: none;
    }

    .control-btn      { width: 30px; height: 30px; font-size: 14px; }
    .control-btn-play { width: 36px; height: 36px; font-size: 17px; }

    /* Shuffle and loop are the two a thumb is least likely to reach for
       mid-track, and the row cannot hold five buttons plus a title at
       375px. */
    #randomBtn,
    #loopBtn {
        display: none;
    }

    .track-cover  { width: 34px; height: 34px; }
    /* The two font-size overrides that were here are gone: both lines are
       Silkscreen now and held at 8px site-wide, so they had no effect. */

    .persistent-player.minimized .player-container {
        padding: 7px 14px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .footer-dot {
        animation: none;
    }

    .footer-links a,
    .footer-legal a,
    .playlist-container,
    .progress-handle {
        transition: none;
    }
}

@media print {
    .persistent-player {
        display: none;
    }
}

/* ------------------------------------------------------------------ *
 * Header controls strip - narrow screens
 * ------------------------------------------------------------------ */

/*
 * header-controls-panel.css stacks this strip into a column below 768px, and
 * splits the user block into a second column below 480px. That made it 169px
 * tall on a 375px phone - theme toggle on one line, name and role on another,
 * a register link on a third, a full-width language box on a fourth - sitting
 * on top of another 60px of wrapper padding. Before any page began, a phone
 * had spent nearly a third of its screen on a theme switch and a language
 * picker.
 *
 * One row instead. Nothing is removed: the switch keeps its label, the
 * account links and the language picker keep their targets, they simply sit
 * beside each other the way they already do on a desktop.
 *
 * No !important is needed - these selectors are the same weight as the ones
 * they correct, and this file loads afterwards. That also means this block
 * outranks that file's own 480px rules, which is deliberate: the two-column
 * split it describes is the layout being replaced.
 */
@media (max-width: 768px) {
    .header-controls-panel {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        flex-wrap: nowrap;
        gap: 10px;
        padding: 8px 4px;
    }

    .header-controls-theme {
        justify-content: flex-start;
        padding: 0;
        border-bottom: none;
        flex: 0 0 auto;
    }

    /*
     * Only the mode you are in. Both labels plus the switch will not fit
     * beside the account links at 375px, and the switch on its own is
     * ambiguous - the label is what says which way it is set.
     */
    .header-controls-theme .theme-label:not(.is-active) {
        display: none;
    }

    .header-controls-user {
        flex-direction: row;
        align-items: center;
        justify-content: flex-end;
        gap: 10px;
        padding: 0;
        text-align: right;
        min-width: 0;
    }

    .header-controls-lang,
    .header-controls-lang-toggle {
        width: auto;
    }

    /*
     * Scale, not just fit.
     *
     * Making this one row solved the height and left everything in it sized
     * for a mouse. Measured on a 375px screen the tap targets were 88x26,
     * 107x22, 68x28 - and Register at 43x7. Seven pixels tall. Every
     * platform's guidance puts the floor near 44px and nothing here was close,
     * so the strip technically fitted and could not really be used.
     *
     * The type is left alone. 8px Silkscreen is the site's UI size, set as
     * --re-size-ui and applied to this panel by theme-color-fix.css, and it is
     * what the footer, the breadcrumbs and every meta line already use. Making
     * this one strip bigger would not be scaling it, it would be breaking step
     * with everything around it.
     *
     * Register is the exception, and only because it does not follow that
     * size: it is pinned to 7px here, a size nothing else on the site uses.
     * !important to match, not to escalate - the declaration it corrects is
     * itself !important, so specificity cannot reach it.
     */
    .header-controls-register {
        font-size: var(--re-size-ui, 8px) !important;
    }

    /*
     * A real target under each control. The label stays its own size; this is
     * the box around it, which is what a thumb actually lands on.
     */
    .header-controls-theme > a,
    .header-controls-profile,
    .header-controls-register,
    .header-controls-lang-toggle {
        display: inline-flex;
        align-items: center;
        min-height: 34px;
    }

    .header-controls-register {
        /* !important for the same reason as the size above: the padding it
           corrects is `padding: 0 !important`, so nothing weaker reaches it. */
        padding: 0 6px !important;
    }

    /*
     * The role badge goes.
     *
     * It renders "guest | Guest" - the role beside a username that, for a
     * signed-out visitor, is the same word. 39px to say Guest twice, on the
     * screen with the least room to say anything. Signed in it reads
     * "administrator | Frank", where the name alone is what identifies you
     * and the role is not news to the person holding the account.
     */
    .header-controls-panel .user-role-display {
        display: none;
    }
}

/*
 * Below 380px - iPhone SE and the narrower Androids - the language picker's
 * border and chevron are more furniture than the row can spare. The globe and
 * the code still say what it is and still open the same menu.
 */
@media (max-width: 380px) {
    .header-controls-lang-toggle {
        border-color: transparent;
        padding-left: 4px;
        padding-right: 4px;
    }

    .header-controls-lang-toggle::after {
        display: none;
    }
}

/* ------------------------------------------------------------------ *
 * Header controls strip - pointer screens
 * ------------------------------------------------------------------ */

/*
 * The same problem above 768px, quieter.
 *
 * Measured at 1280px the strip's targets were 152x26, 107x22, 68x28 - and
 * Register at 48x8. A mouse is far more forgiving than a thumb, but WCAG's
 * floor is 24x24 and half of these sat under it. An eight-pixel-high link is
 * not a target, it is a line of text you have to aim at.
 *
 * 28px, which is what the language pill already measures. Choosing the
 * tallest control means the panel's own height does not move: its content box
 * is 29px and the pill was already filling it, so every control grows into
 * space the strip had already reserved. Nothing shifts, nothing reflows, and
 * the difference is only felt by the pointer.
 *
 * The labels are untouched. 8px Silkscreen is --re-size-ui, the size the
 * footer and the breadcrumbs use, and this strip should look like they do.
 */
@media (min-width: 769px) {
    .header-controls-theme > a,
    .header-controls-profile,
    .header-controls-register,
    .header-controls-lang-toggle {
        min-height: 28px;
    }

    /* Register carries no background, so its target is exactly its text.
       A little padding is the only thing that widens it. */
    .header-controls-register {
        padding: 0 6px !important;
    }

    /*
     * The role badge is the desktop's 7px outlier - the same fault Register
     * had, from user-role-colors.css rather than from here. It sits inside
     * the profile link at a size nothing else on the site uses; 8px puts it
     * back in step with the strip it is printed on.
     */
    .header-controls-panel .user-role-display {
        font-size: var(--re-size-ui, 8px);
    }
}

/* ------------------------------------------------------------------ *
 * Soft navigation
 * ------------------------------------------------------------------ */

/*
 * A page change no longer reloads the browser, so the browser no longer shows
 * that anything is happening - no spinner in the tab, no progress bar. On a
 * slow connection a click would look ignored, and the second click would be
 * a second request.
 *
 * This is that missing feedback and nothing more: a hairline across the top
 * in the palette's own accent, only while a fetch is in flight.
 */
html.is-softnav::after {
    content: "";
    position: fixed;
    top: 0;
    left: 0;
    height: 2px;
    width: 100%;
    z-index: 20000;
    background: linear-gradient(90deg,
        transparent 0%,
        var(--re-brand-200) 40%,
        var(--re-brand-100) 50%,
        var(--re-brand-200) 60%,
        transparent 100%);
    animation: re-softnav-sweep 1s linear infinite;
    pointer-events: none;
}

@keyframes re-softnav-sweep {
    from { transform: translateX(-100%); }
    to   { transform: translateX(100%); }
}

/* The bar is a progress hint, not decoration: hold it still rather than
   removing it, so it still says "working" without the movement. */
@media (prefers-reduced-motion: reduce) {
    html.is-softnav::after { animation: none; }
}

/* While the new page is being fetched the old one is still interactive, which
   invites a second click on a link that is already loading. */
html.is-softnav a { cursor: progress; }

/* ==========================================================================
   The breadcrumb's live dot
   ==========================================================================

   These three classes were the ONLY thing on the site using animate.css, and
   that file is 37KB of a hundred animations to supply them - loaded on every
   page, for one blinking dot in the breadcrumb.

   Copied verbatim from animate.css 3.5.1 rather than rewritten, so the dot
   keeps exactly the timing it had. The names are kept too: the markup in
   includes/components/breadcrumb.php says `flash animated infinite`, and
   bs-init.js removes `animated` by name.
   ========================================================================== */

.animated {
    animation-duration: 1s;
    animation-fill-mode: both;
}

.animated.infinite,
.infinite {
    animation-iteration-count: infinite;
}

.flash {
    animation-name: flash;
}

@keyframes flash {
    0%, 50%, to { opacity: 1; }
    25%, 75%    { opacity: 0; }
}

/* One blink a second is a lot next to text; the dot is decoration, and a
   visitor who asked for less motion should simply see it lit. */
@media (prefers-reduced-motion: reduce) {
    .animated { animation: none; }
}


/* ===== assets/css/page-refined.css ===== */
/**
 * Inner pages: Projects, Lab, Music, About, Resume, Contact.
 * ==========================================================
 *
 * The home page was rebuilt on a small set of rules - an eyebrow above a
 * title, one hairline between sections, quiet cards, and three type faces
 * with one job each. This carries that to the other six pages. It is the
 * counterpart to home-refined.css (page bodies) and site-chrome.css (header,
 * footer, player), and it loads after both.
 *
 *
 * WHAT WAS WRONG
 *
 * Two things, on every page.
 *
 * First, boxes inside boxes. A page opened with a rounded, bordered, blurred
 * panel holding the title; beneath it another for the filters; then a grid of
 * cards with the same border and radius; and on Projects the breadcrumb above
 * all of them was a fourth. Four levels of one treatment, so none of them
 * meant anything - a border stops separating content from its surroundings
 * once the surroundings have the same border. The home page uses that
 * treatment once, on cards, and a hairline everywhere else.
 *
 * Second, five different mastheads. Projects used .page-header; Lab and Music
 * each carried an inline copy of the same 60px icon tile and an <h1> pinned
 * to "Albert Sans" 42px #826b7d; Resume and About had two more. All of them
 * put a Silkscreen line UNDER the title as a subtitle, where the home page
 * puts it above as an eyebrow. They are now one component - see
 * includes/components/page-head.php.
 *
 *
 * TYPE ROLES - the same three, unchanged from home-refined.css
 *
 *   Silkscreen   small caps labels only: eyebrows, meta, counts, tags. A
 *                pixel face reads as craft at 9-11px and as noise at
 *                paragraph size, so it never sets running text.
 *   ThaleahFat   display headings.
 *   Albert Sans  everything a person actually reads.
 *
 * No colours are introduced here. The ramp is the existing --re-brand-*, and
 * the hairline is --home-rule, the same one under the theme strip, between
 * home page sections and above the footer.
 */

/* ------------------------------------------------------------------ *
 * Visibility
 * ------------------------------------------------------------------ */

/*
 * The entrance animations are kept, and the reason they used to fail is fixed
 * at its source in assets/js/bs-init.js - AOS now re-measures whenever the
 * page height changes, instead of trusting one reading taken before the lazy
 * images had any height. See the note at the top of that file.
 *
 * There is deliberately no CSS override here forcing [data-aos] visible. One
 * existed briefly and it worked, but it worked by disabling the animations
 * altogether, which is a poor trade for something that can be fixed properly.
 *
 * If a page is ever reported blank again, the first thing to check is whether
 * AOS.refresh() is still being called on height changes; the second is
 * whether a new element carries data-aos inside a container that AOS cannot
 * measure, such as one that is display:none at init.
 */

/*
 * A print stylesheet must not depend on any of that, since none of the scroll
 * machinery runs for a print rendering.
 */
@media print {
    [data-aos] {
        opacity: 1 !important;
        transform: none !important;
    }
}

/* ------------------------------------------------------------------ *
 * Shared tokens
 * ------------------------------------------------------------------ */

/*
 * Declared on the section too, not only on the masthead and body.
 *
 * These are custom properties, so they reach an element only by inheritance.
 * .page-section is a SIBLING of .page-head on the resume and about pages -
 * not a descendant - so every var(--page-gap) inside a section resolved to
 * nothing, and an unresolvable var() is not "ignore this declaration", it is
 * "use the property's initial value". For margin-bottom that is 0, which is
 * why the rule under each section title sat flush on the content below it.
 */
.page-head,
.page-body,
.page-section {
    --page-rule: var(--home-rule, rgba(130, 107, 125, 0.22));
    --page-gap: clamp(24px, 3vw, 40px);
}

/* ------------------------------------------------------------------ *
 * Masthead
 * ------------------------------------------------------------------ */

.page-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 24px;
    align-items: end;
    padding: clamp(20px, 3vw, 40px) 0 clamp(18px, 2.4vw, 28px);
    margin-bottom: clamp(28px, 4vw, 48px);
    border-bottom: 1px solid var(--page-rule);
}

.page-head-eyebrow {
    display: block;
    font-family: var(--re-font-ui);
    font-size: 0.64rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    margin-bottom: 12px;
}

/*
 * The !important is against theme-color-fix.css, which sets the display face
 * on h1..h6 with !important of its own. That is right for headings generally;
 * it just cannot be argued with from a class selector without matching it.
 * The size is the argument here, not the face.
 */
.page-head-title {
    font-family: var(--re-font-display) !important;
    font-size: clamp(2.1rem, 5vw, 3.4rem);
    line-height: 1;
    letter-spacing: 0.01em;
    margin: 0;
    color: var(--re-text-color);
    /* The reactive background moves behind this, so the type needs its own
       weight against a field that is not still. */
    text-shadow: 0 2px 20px rgba(0, 0, 0, 0.5);
}

.page-head-lead {
    font-family: var(--re-font-body);
    font-size: clamp(0.98rem, 1.3vw, 1.12rem);
    line-height: 1.55;
    color: var(--re-text-color);
    opacity: 0.72;
    max-width: 62ch;
    margin: 16px 0 0;
}

.page-head-side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 14px;
    padding-bottom: 4px;
}

.page-head-meta {
    display: flex;
    align-items: center;
    gap: 10px;
    font-family: var(--re-font-ui);
    font-size: 0.6rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    white-space: nowrap;
}

.page-head-meta-dot {
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.7;
}

.page-head-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* ------------------------------------------------------------------ *
 * Sections
 * ------------------------------------------------------------------ */

.page-section {
    margin-bottom: clamp(40px, 6vw, 72px);
}

.page-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
    /* Fallbacks on both, and not for tidiness: without them an element that
       sits outside every scope declaring these tokens gets margin-bottom 0
       and a border in currentColor, which is a white hairline hard against
       the content. Both failures are silent. */
    margin-bottom: var(--page-gap, clamp(24px, 3vw, 40px));
    padding-bottom: 14px;
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

.page-section-title {
    font-family: var(--re-font-display) !important;
    font-size: clamp(1.3rem, 2.2vw, 1.75rem);
    line-height: 1.1;
    margin: 0;
    color: var(--re-text-color);
}

.page-section-note {
    font-family: var(--re-font-ui);
    font-size: 0.6rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.45;
}

/* ------------------------------------------------------------------ *
 * De-carding
 * ------------------------------------------------------------------ */

/*
 * The panels that used to hold each page's title. The markup is gone from the
 * templates, but these three classes are still referenced by other pages and
 * by page-standard.css, so they are neutralised rather than deleted: any that
 * survive somewhere become a plain block instead of a fifth nested card.
 */
.page-header,
.lab-header,
.music-header {
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    padding: 0 !important;
}

/*
 * Filter and control strips. These sat in their own bordered, blurred panel
 * directly beneath the title panel - two cards before a visitor reached a
 * single project. A row of controls under a hairline reads as a toolbar for
 * the list below it, which is what it is.
 */
.filter-bar,
.page-toolbar {
    background: none !important;
    border: none !important;
    border-top: 1px solid var(--home-rule, rgba(130, 107, 125, 0.22)) !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    padding: 20px 0 0 !important;
    margin-bottom: clamp(28px, 4vw, 44px) !important;
}

/*
 * The breadcrumb. On an inner page it sat above the title as a full-width
 * bordered pill - a card whose entire content is "HOME / PROJECTS". It keeps
 * its place in the reading order and loses the box.
 */
.breadcrumb {
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    padding: 0 !important;
    margin-bottom: 0 !important;
}

/*
 * THE PSEUDO-ELEMENTS HAVE TO GO WITH THE BOX.
 *
 * Clearing background, border and radius is only half of removing a glass
 * panel. glass-standard.css also gives each of these a ::before holding a
 * full-bleed noise texture and an ::after drawing a bright 1px highlight
 * across the top edge, and breadcrumb-standard.css adds its own pair. Those
 * kept painting after the box beneath them was gone, so what was left was a
 * faint hard-cornered rectangle of noise with a lit strip along its top,
 * attached to nothing. It read as a rendering artifact because that is
 * effectively what it was.
 *
 * The noise inherits border-radius, which made it worse: with the radius
 * forced to 0 the ghost had square corners while everything near it was
 * round.
 *
 * Anything de-carded above has to be listed here too.
 */
.breadcrumb::before,
.breadcrumb::after,
.page-header::before,
.page-header::after,
.lab-header::before,
.lab-header::after,
.music-header::before,
.music-header::after,
.filter-bar::before,
.filter-bar::after,
body[data-page="portfolio"] .container.glass::before,
body[data-page="portfolio"] .container.glass::after {
    content: none !important;
}

/*
 * The trail also sits above a gradient rule drawn by breadcrumb.php, which
 * lands as a second hairline immediately above the masthead's own. One line
 * is enough.
 */
.breadcrumb + .article-separator {
    display: none;
}

/* ------------------------------------------------------------------ *
 * Controls
 * ------------------------------------------------------------------ */

.filter-btn,
.page-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px;
    border: 1px solid var(--page-rule);
    border-radius: var(--re-radius-pill, 999px);
    background: transparent;
    color: var(--re-text-color);
    font-family: var(--re-font-ui);
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    text-decoration: none;
    opacity: 0.72;
    transition: opacity 0.16s ease, background 0.16s ease, border-color 0.16s ease;
}

.filter-btn:hover,
.page-chip:hover {
    opacity: 1;
    color: var(--re-text-color);
    background: rgba(var(--re-brand-400-rgb), 0.14);
}

.filter-btn.active,
.page-chip.active {
    opacity: 1;
    color: var(--re-text-color);
    background: rgba(var(--re-brand-400-rgb), 0.24);
    border-color: var(--re-brand-400);
}

.view-toggle-btn {
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--page-rule);
    border-radius: var(--re-radius-sm, 6px);
    background: transparent;
    color: var(--re-text-color);
    opacity: 0.6;
    cursor: pointer;
    transition: opacity 0.16s ease, background 0.16s ease;
}

.view-toggle-btn:hover { opacity: 1; }

.view-toggle-btn.active {
    opacity: 1;
    background: rgba(var(--re-brand-400-rgb), 0.22);
    border-color: var(--re-brand-400);
}

/*
 * The solid variant, for the one action on a page that is the point of it -
 * Download on the resume.
 */
.page-chip-solid {
    background: rgba(var(--re-brand-400-rgb), 0.26);
    border-color: var(--re-brand-400);
    opacity: 1;
}

.page-chip-solid:hover {
    background: rgba(var(--re-brand-400-rgb), 0.4);
    border-color: var(--re-brand-200);
}

/* ------------------------------------------------------------------ *
 * Resume
 * ------------------------------------------------------------------ */

/*
 * Email, phone, website, LinkedIn, GitHub. Reference information, so it sits
 * quietly under a hairline rather than as five buttons in the masthead.
 */
.resume-contact {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 24px;
    padding-bottom: 22px;
    margin-bottom: clamp(32px, 5vw, 56px);
    border-bottom: 1px solid var(--page-rule);
    font-family: var(--re-font-body);
    font-size: 0.88rem;
}

.resume-contact a,
.resume-contact span {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--re-text-color);
    opacity: 0.66;
    text-decoration: none;
    transition: opacity 0.16s ease;
}

.resume-contact a:hover {
    opacity: 1;
    color: var(--re-text-color);
}

.resume-contact i {
    color: var(--re-brand-300);
    font-size: 0.95em;
}

/* The dropdown wrapper is a span in a flex row; it must not inherit the
   muted treatment meant for the plain items beside it. */
.resume-contact .dropdown {
    opacity: 1;
}

/* ------------------------------------------------------------------ *
 * About
 * ------------------------------------------------------------------ */

/*
 * About keeps its own opening rather than being given a masthead, because it
 * already has one: a portrait, a name and an intro, which is the same shape
 * as the home page hero. What it did not have was the site's order or its
 * type - so the panel goes, the subtitle moves above the title as an eyebrow,
 * and the title comes up to masthead scale.
 */
.about-hero,
.about-hero-container {
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    padding: 0 !important;
}

.about-hero {
    padding-bottom: clamp(28px, 4vw, 44px) !important;
    border-bottom: 1px solid var(--home-rule, rgba(130, 107, 125, 0.22)) !important;
}

/*
 * column-reverse rather than reordered markup: the subtitle follows the title
 * in the HTML, which is the correct reading order for a screen reader and for
 * a page with no CSS. Only the painted order changes.
 */
.about-hero .intro-title,
.about-hero .intro-subtitle {
    margin: 0;
}

.intro-title {
    font-family: var(--re-font-display) !important;
    font-size: clamp(2rem, 4.4vw, 3rem) !important;
    line-height: 1 !important;
    color: var(--re-text-color) !important;
    order: 2;
}

.intro-subtitle {
    font-family: var(--re-font-ui) !important;
    font-size: 0.64rem !important;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--re-text-color) !important;
    opacity: 0.5;
    order: 1;
    margin-bottom: 12px !important;
}

.intro-content {
    font-family: var(--re-font-body);
    font-size: clamp(0.98rem, 1.3vw, 1.1rem);
    line-height: 1.65;
    color: var(--re-text-color);
    opacity: 0.75;
    order: 3;
    margin-top: 18px !important;
    max-width: 62ch;
}

/* The intro column is the flex context those orders apply in. */
.about-hero .col-md-8,
.about-hero .col-md-7 {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

/*
 * Section headings on About and on the gaming carousels. Same inversion: the
 * .section-meta line was a subtitle beneath the title and becomes the eyebrow
 * above it.
 */
.section-header-accent {
    border: none !important;
    background: none !important;
    padding: 0 0 14px !important;
    margin-bottom: var(--page-gap, 32px) !important;
    border-bottom: 1px solid var(--home-rule, rgba(130, 107, 125, 0.22)) !important;
}

.section-header-text {
    display: flex;
    flex-direction: column;
}

.section-header-text .section-title {
    font-family: var(--re-font-display) !important;
    font-size: clamp(1.3rem, 2.2vw, 1.75rem) !important;
    line-height: 1.1 !important;
    margin: 0 !important;
    color: var(--re-text-color) !important;
    order: 2;
}

.section-header-text .section-meta,
.section-header-text .section-subtitle {
    font-family: var(--re-font-ui) !important;
    font-size: 0.6rem !important;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--re-text-color) !important;
    opacity: 0.45;
    order: 1;
    margin: 0 0 10px !important;
}

.section-header-text .section-title i {
    color: var(--re-brand-300);
    font-size: 0.8em;
}

/*
 * The content sections further down About - "Who I Am", "Skills &
 * Technologies", the experience list. These are not wrapped in
 * .section-header-text: the title and subtitle are plain siblings inside
 * [data-about-section], so they need their own inversion.
 *
 * Everything gets order 3 first, then the two headings are pulled in front of
 * it. Without that base, any child this file does not name would default to
 * order 0 and jump above the heading - a new section type added later would
 * render its body before its title.
 */
[data-about-section] {
    display: flex;
    flex-direction: column;
    align-items: stretch;
}

[data-about-section] > * {
    order: 3;
}

[data-about-section] > .section-subtitle {
    order: 1;
    font-family: var(--re-font-ui) !important;
    font-size: 0.6rem !important;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--re-text-color) !important;
    opacity: 0.45;
    margin: 0 0 10px !important;
}

[data-about-section] > .section-title {
    order: 2;
    font-family: var(--re-font-display) !important;
    font-size: clamp(1.3rem, 2.2vw, 1.75rem) !important;
    line-height: 1.1 !important;
    color: var(--re-text-color) !important;
    margin: 0 0 var(--page-gap, 32px) !important;
    padding-bottom: 14px !important;
    border-bottom: 1px solid var(--home-rule, rgba(130, 107, 125, 0.22));
}

[data-about-section] > .section-content {
    font-family: var(--re-font-body);
    font-size: 1rem;
    line-height: 1.7;
    color: var(--re-text-color);
    opacity: 0.78;
    max-width: 68ch;
}

/*
 * The sections keep their panels.
 *
 * These were stripped to bare text on a hairline, on the same reasoning that
 * removed the panels elsewhere, and it was wrong here. On Projects or
 * Portfolio the panel wrapped a grid of cards that already had borders of
 * their own, so it was a box around boxes. On About each section is a block
 * of running prose, and the panel is the only thing giving it an edge -
 * without one the page is an undifferentiated column of text, and the rounded
 * corner that belonged to the design goes with it.
 *
 * What does change is that "contained" and "open" no longer differ. They were
 * two surfaces at different opacities alternating down the page, so every
 * other section read as a different kind of thing. One treatment for both.
 */
.about-section,
.about-section.contained,
.about-section.open,
.gaming-section {
    background: var(--re-surface) !important;
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: blur(16px) saturate(170%) !important;
    backdrop-filter: blur(16px) saturate(170%) !important;
    border: 1px solid var(--re-border) !important;
    border-radius: var(--re-radius-lg) !important;
    box-shadow: 0 6px 28px rgba(0, 0, 0, 0.26) !important;
    padding: clamp(24px, 3.4vw, 40px) !important;
}

/* Inside a panel the heading rule is an underline for the heading, not a line
   cutting the panel in half, so it takes the panel's own border colour. */
[data-about-section] > .section-title {
    border-bottom-color: var(--re-border);
}

/* ------------------------------------------------------------------ *
 * Gaming carousels
 * ------------------------------------------------------------------ */

/*
 * Edge fade, and room for the hover-lift.
 *
 * about-page.css originally faded the ends of the carousel with two colour
 * gradients running to --re-ground-outer. A colour gradient only blends on a
 * surface of exactly that colour, and this sits on translucent glass over the
 * page's moving background - so it painted two grey smears matching nothing,
 * each with a visible vertical seam. That overlay is killed here.
 *
 * The fade is a mask on the scroller instead: it drops the cards' own alpha to
 * zero at each end, so they dissolve into whatever is actually behind them,
 * moving background included, with no colour to match. Only the end that has
 * more cards past it is faded, driven by the controller's
 * at-start / at-end / is-scrollable classes.
 *
 * The scroller also gets vertical padding. overflow-x scrolling forces the
 * cross axis to clip, which was shearing the top off the -8px hover-lift and
 * cutting its drop shadow; the padding gives both room inside the clip box.
 */
.games-carousel-container::before,
.games-carousel-container::after {
    content: none !important;
}

.games-carousel {
    padding-top: 16px;
    padding-bottom: 40px;
}

.games-carousel-container.is-scrollable .games-carousel {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 56px, #000 calc(100% - 56px), transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 56px, #000 calc(100% - 56px), transparent 100%);
}

/* At the left end, only the right edge has more to reveal. */
.games-carousel-container.is-scrollable.at-start .games-carousel {
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 56px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 56px), transparent 100%);
}

/* At the right end, only the left edge does. */
.games-carousel-container.is-scrollable.at-end .games-carousel {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 56px, #000 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 56px, #000 100%);
}

/* ------------------------------------------------------------------ *
 * Compact poster tiles
 *
 * The cards were 280px wide with the 2:3 cover (417px tall) and the whole
 * title / tags / progress / "why it's special" / "memorable moment" stack
 * sitting BELOW it - which ran a favourite card past 1000px and the two
 * shelves to ~2160px, most of it text under the art.
 *
 * The card is a poster now: the art fills it and the text is a frosted panel
 * over the foot of the art. At rest it shows the title and tags; on hover it
 * slides up, and the blur behind it keeps the detail legible over any cover.
 * Nothing stacks below, so a card is just the height of its poster - a little
 * over half the old footprint.
 * ------------------------------------------------------------------ */
.game-card {
    flex: 0 0 clamp(160px, 16vw, 210px) !important;
    position: relative !important;
}

/* The media is the whole card, so it rounds on all four corners (about-page
   and prevent-effect-clipping round only the top, from when text sat below). */
.game-card .media {
    aspect-ratio: 2 / 3 !important;
    border-radius: var(--re-radius-lg) !important;
}

.game-card .media img { object-fit: cover !important; }

/* Badges: top corner, inset from the art edge, clear of the caption. */
.game-card .status-badge,
.game-card .rating-badge {
    top: 12px !important;
    right: 12px !important;
    bottom: auto !important;
    left: auto !important;
}

/*
 * Caption / detail panel.
 *
 * Frosted glass (a real backdrop blur) rather than a flat scrim, so the text
 * stays legible over light or busy box art. The panel is never taller than the
 * poster (max-height: 100%): at rest only the title and tags show - the detail
 * blocks below collapse to nothing - and on hover they expand within the panel.
 *
 * This is what fixes the clipping: the old version capped the panel at a fixed
 * height (5em, then calc(100% - 52px)) and hid the overflow, so a card with a
 * couple of paragraphs had its lower lines sheared off. Now the panel sizes to
 * the poster, the detail expands inside it, and overflow switches to auto on
 * hover - so the rare card whose notes still overrun scrolls instead of being
 * cut. overflow carries !important because prevent-effect-clipping.css forces
 * .content overflow visible.
 */
.game-card .content {
    position: absolute !important;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex !important;
    flex-direction: column;
    gap: 0 !important;
    padding: 13px 15px !important;
    background: rgba(8, 5, 10, 0.5) !important;
    -webkit-backdrop-filter: blur(14px) saturate(140%);
            backdrop-filter: blur(14px) saturate(140%);
    border-top: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: 0 0 var(--re-radius-lg) var(--re-radius-lg);
    max-height: 100%;
    overflow: hidden !important;
    transition: background 0.42s ease;
}

.game-card:hover .content,
.game-card:focus-within .content {
    overflow-y: auto !important;
    background: rgba(8, 5, 10, 0.72) !important;
}

/* Title and tags are the resting caption - always visible. */
.game-card .content > .title {
    margin: 0 !important;
    font-size: 0.95rem !important;
    line-height: 1.15 !important;
}

.game-card .content > .tags {
    margin-top: 8px !important;
    gap: 5px !important;
}

/*
 * Every other block - progress, hours, notes, "why it's special", "memorable
 * moment" - is detail: collapsed to nothing at rest, revealed on hover.
 * Collapsing the top margin as well as the height (instead of a flex gap on the
 * panel) means the collapsed blocks leave no empty gaps in the resting caption.
 * The old boxed backgrounds/borders are dropped - the frosted panel is the
 * container now, so those would be a box inside a box.
 */
.game-card .content > *:not(.title):not(.tags) {
    max-height: 0;
    opacity: 0;
    margin-top: 0;
    overflow: hidden;
    background: transparent !important;
    border: none !important;
    padding: 0 !important;
    transition: max-height 0.42s cubic-bezier(0.4, 0, 0.2, 1),
                opacity 0.3s ease, margin-top 0.42s ease;
}

.game-card:hover .content > *:not(.title):not(.tags),
.game-card:focus-within .content > *:not(.title):not(.tags) {
    max-height: 24em;
    opacity: 1;
    margin-top: 10px;
}

/* Touch has no hover, so reveal the detail at rest rather than stranding it
   behind a gesture the device cannot make. */
@media (hover: none) {
    .game-card .content { overflow-y: auto !important; }
    .game-card .content > *:not(.title):not(.tags) {
        max-height: 24em;
        opacity: 1;
        margin-top: 10px;
    }
}

/* The little label over each note (WHY IT'S SPECIAL / MEMORABLE MOMENT). */
.game-card .content .label { margin-bottom: 4px !important; }
.game-card .content .note { margin: 0 !important; }

/* Thin scrollbar for the rare card whose notes overrun the poster on hover. */
.game-card .content::-webkit-scrollbar { width: 5px; }
.game-card .content::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.28);
    border-radius: 5px;
}

/* ------------------------------------------------------------------ *
 * Contact
 * ------------------------------------------------------------------ */

/*
 * Portfolio and Contact both wrapped their whole body in a full-width glass
 * panel that also held the page heading. The heading is the masthead now, and
 * the two pages part company here.
 *
 * Portfolio is a grid of cards carrying their own borders, so its panel was a
 * box drawn around boxes and it goes entirely.
 *
 * Contact is a single form, which needs an edge to sit against - the fields
 * are the content and they do not frame themselves. Its panel stays, with the
 * rounded corner it always had. Only the heading has left it.
 */
body[data-page="portfolio"] .container.glass {
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
}

body[data-page="contact"] .container.glass {
    border-radius: var(--re-radius-lg) !important;
}

/* ------------------------------------------------------------------ *
 * Narrow screens
 * ------------------------------------------------------------------ */

@media (max-width: 768px) {
    /*
     * A gutter. The home page has one; the inner pages ran their content to
     * the very edge of the screen because the breadcrumb container sets its
     * side padding to 0 inline, and nothing put it back.
     */
    body:not([data-page="home"]) .container.effect {
        padding-left: 16px !important;
        padding-right: 16px !important;
    }

    /*
     * Sideways scroll on Music.
     *
     * The page is a two-column grid, "280px 1fr", collapsed to a single 1fr
     * column on a phone by a rule in the template. But a 1fr track is
     * min-width:auto by default, so it will not shrink below its widest
     * child - and the widest child is a track title like "Super Street
     * Fighter II Turbo - Guiles Stage". The column stayed 394px wide inside a
     * 375px screen and took the whole document with it: the navbar, the
     * player bar and the footer all measured 409.
     *
     * min-width: 0 lets the track shrink; the ellipsis rules below give the
     * text somewhere to go when it does.
     */
    body[data-page="music"] [style*="grid-template-columns"] > * {
        min-width: 0;
    }

    /*
     * The track row holds five things: index, art, title/artist, a genre
     * badge and a duration. At 375px that leaves the title about 33px, so
     * "Super Street Fighter II Turbo - Guiles Stage" arrives as "Super…".
     *
     * The index and the badge are the two that carry least: the row order is
     * already visible, and the genre is a filter in the sidebar above. Drop
     * those, halve the art, tighten the padding, and the title gets roughly
     * 200px - enough to be a title rather than a hint of one.
     */
    .track-item {
        min-width: 0;
        gap: 12px !important;
        padding: 12px 4px !important;
    }

    .track-index,
    .track-genre {
        display: none !important;
    }

    .track-art {
        width: 44px !important;
        height: 44px !important;
        flex: 0 0 auto;
    }

    .track-meta {
        min-width: 0;
    }

    .track-duration {
        min-width: 40px !important;
        flex: 0 0 auto;
    }

    .page-head {
        grid-template-columns: minmax(0, 1fr);
        gap: 16px;
        align-items: start;
    }

    /*
     * Counts and controls move under the title rather than being squeezed
     * into a second column beside a heading set at 5vw.
     */
    .page-head-side {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        width: 100%;
        padding-bottom: 0;
    }

    .page-head-lead {
        font-size: 0.95rem;
    }

    .page-section-head {
        gap: 8px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .filter-btn,
    .page-chip,
    .view-toggle-btn {
        transition: none;
    }
}

/* ------------------------------------------------------------------ *
 * Platform badges
 * ------------------------------------------------------------------ */

/*
 * The consoles a project shipped on, as their own wordmarks. See
 * includes/components/platform-badges.php.
 *
 * Sized by HEIGHT and never by width. The logos are single-colour white SVGs
 * with very different aspect ratios - Xbox Series is 3839x1575, Xbox 360 is
 * 3786x784 - so a fixed width would render one of them tiny and the other
 * enormous. A common height is what makes a row of different wordmarks read as
 * one set.
 */
.platform-badges {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 18px;
    list-style: none;
    margin: 14px 0 0;
    padding: 0;
}

.platform-badge {
    display: inline-flex;
    align-items: center;
}

.platform-badge img {
    height: 15px;
    width: auto;
    max-width: 150px;
    display: block;
    /* The wordmarks are pure white; at full strength they out-shout the
       headings above them, so they sit back until hovered. */
    opacity: 0.62;
    transition: opacity 0.18s ease;
}

.platform-badges:hover .platform-badge img,
.platform-badge img:hover {
    opacity: 0.95;
}

/*
 * A platform with no logo yet. Shown rather than dropped: omitting it would
 * quietly claim the project did not ship there.
 */
.platform-badge-text {
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    padding: 3px 9px;
    border: 1px solid var(--home-rule, rgba(130, 107, 125, 0.22));
    border-radius: var(--re-radius-pill, 999px);
}

/* On a card the row is one line of small marks, not a feature in itself. */
.home-card .platform-badges,
.portfolio-card .platform-badges {
    gap: 8px 12px;
    margin-top: 10px;
}

.home-card .platform-badge img,
.portfolio-card .platform-badge img {
    height: 12px;
    max-width: 110px;
}

@media (max-width: 768px) {
    .platform-badge img { height: 13px; max-width: 120px; }
}

@media (prefers-reduced-motion: reduce) {
    .platform-badge img { transition: none; }
}

/* ------------------------------------------------------------------ *
 * Portfolio grid
 * ------------------------------------------------------------------ */

/*
 * Replaces a Bootstrap .row of .card inside a .container.glass.
 *
 * The problems it fixes, in the order they were visible:
 *
 *   Cards ended wherever their text ended, so three in a row finished at three
 *   different heights and no two buttons lined up. Each card is a flex column
 *   now and .pf-actions carries margin-top:auto, which pushes the buttons to
 *   the bottom of whatever space the card has.
 *
 *   The platform wordmarks sat flush against the buttons - 12px marks with a
 *   button row that had no top margin - so they read as overlapping them.
 *   .pf-platforms has its own space above and below.
 *
 *   And the whole grid sat inside a glass panel, which was a box drawn around
 *   a set of boxes. Gone; the cards are the only surface.
 */

.pf-grid {
    display: grid;
    /* auto-fill rather than a fixed column count: the grid decides how many
       fit, so it works at every width without breakpoints. */
    grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
    gap: clamp(20px, 2.4vw, 32px);
    list-style: none;
    margin: 0 0 clamp(48px, 7vw, 90px);
    padding: 0;
}

/*
 * The portfolio card.
 *
 * This was a flat panel: a hardcoded plum at 50% and no blur, while the
 * project cards next door used the site's standard recipe and had both. Two
 * grids of the same thing, one of them glass and one of them not.
 *
 * It is the standard recipe now - the same three tokens .standard-card uses
 * in page-standard.css - so the two agree, and the card follows the palette
 * instead of staying purple under every theme.
 */
.pf-card {
    display: flex;
    flex-direction: column;
    background: var(--re-surface);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-lg, 14px);
    overflow: hidden;
    transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}

.pf-card:hover {
    border-color: var(--re-border-strong);
    transform: translateY(-3px);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}

/* --- media -------------------------------------------------------- */

.pf-media {
    display: block;
    /* A ratio rather than a fixed height: every card's image is the same
       shape, and the shape holds as the column width changes. */
    aspect-ratio: 16 / 10;
    background: rgba(var(--re-brand-400-rgb), 0.12);
    overflow: hidden;
}

.pf-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.4s ease;
}

.pf-card:hover .pf-media img { transform: scale(1.03); }

.pf-media-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.35;
}

/* --- body --------------------------------------------------------- */

.pf-body {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 18px 18px 20px;
    flex: 1 1 auto;
}

/*
 * The mark on work that never shipped - cancelled, shelved, a prototype.
 *
 * Only the exception is marked. `published` decides whether the entry is on
 * the site at all; this says what happened to the work, and labelling the
 * released majority "Released" would be noise to caveat the few.
 *
 * No font-size: the pixel grid owns the size of every Silkscreen face.
 */
.pf-release {
    align-self: flex-start;
    margin: 0 0 8px;
    padding: 4px 9px;
    border: 1px solid currentColor;
    border-radius: var(--re-radius-pill, 999px);
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    opacity: 0.9;
}

.pf-release-unreleased { color: #e0a13a; }
.pf-release-cancelled  { color: #ffb4a8; }
.pf-release-prototype  { color: #6cc4db; }

.pf-title {
    font-family: var(--re-font-display) !important;
    font-size: 1.05rem;
    line-height: 1.15;
    margin: 0;
    color: var(--re-text-color);
}

.pf-title a { color: inherit; text-decoration: none; }
.pf-title a:hover { color: var(--re-brand-100); }

.pf-desc {
    font-family: var(--re-font-body);
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--re-text-color);
    opacity: 0.72;
    margin: 0;
    /* Clamped rather than truncated in PHP: the whole description stays in the
       DOM for search and for a screen reader, and no ellipsis is added to text
       that did not need one. */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.pf-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.pf-tags li {
    font-family: var(--re-font-ui);
    /* Silkscreen has no '#', so C# renders as a missing glyph at label sizes.
       Body face for anything that can contain punctuation. */
    font-size: 0.6rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.6;
    padding: 3px 8px;
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-pill, 999px);
}

/* --- platforms ---------------------------------------------------- */

/*
 * Its own band, with a hairline above. The marks are small and white; without
 * separation they read as part of whatever is next to them - which is exactly
 * how they ended up looking like they were on top of the buttons.
 */
.pf-platforms {
    padding-top: 12px;
    border-top: 1px solid var(--re-border-faint);
}

.pf-platforms .platform-badges { margin: 0; gap: 8px 14px; }
.pf-platforms .platform-badge img { height: 13px; max-width: 96px; opacity: 0.7; }
.pf-platforms .platform-badge img:hover { opacity: 1; }

/* --- actions ------------------------------------------------------ */

/*
 * margin-top:auto is the whole trick: it takes the free space in the card and
 * puts it above this row, so every card in a row ends level however long its
 * description happens to be.
 */
.pf-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: auto;
    padding-top: 14px;
}

.pf-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px;
    border-radius: var(--re-radius-sm, 6px);
    border: 1px solid var(--re-border-strong);
    background: rgba(93, 75, 96, 0.35);
    color: var(--re-text-color);
    font-family: var(--re-font-ui);
    font-size: 0.58rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    text-decoration: none;
    transition: background 0.16s ease, border-color 0.16s ease;
}

.pf-btn:hover {
    background: rgba(var(--re-brand-400-rgb), 0.4);
    border-color: var(--re-brand-200);
    color: var(--re-text-color);
}

.pf-btn-primary {
    background: rgba(var(--re-brand-400-rgb), 0.42);
    border-color: var(--re-brand-400);
}

.pf-btn i { font-size: 0.85em; opacity: 0.8; }

/* --- empty state -------------------------------------------------- */

.pf-empty {
    text-align: center;
    padding: 60px 20px 90px;
    color: var(--re-text-color);
}

.pf-empty i { font-size: 48px; opacity: 0.3; display: block; margin-bottom: 14px; }
.pf-empty p { font-family: var(--re-font-body); opacity: 0.6; margin: 0; }

@media (max-width: 480px) {
    .pf-grid { grid-template-columns: minmax(0, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
    .pf-card,
    .pf-media img { transition: none; }
    .pf-card:hover { transform: none; }
    .pf-card:hover .pf-media img { transform: none; }
}

/* ------------------------------------------------------------------ *
 * Music
 * ------------------------------------------------------------------ */

/*
 * Replaces a 280px sidebar + `1fr` grid, three glass panels and about sixty
 * inline styles.
 *
 * The bug that made this urgent: the track list rendered ~315px wider than the
 * page, past the masthead rule and off the right edge. `1fr` is shorthand for
 * `minmax(auto, 1fr)`, and that `auto` minimum is the column's MIN-CONTENT -
 * which the widest row put at 1150px against 836px of available space, so the
 * column simply refused to be narrower and overflowed the grid.
 *
 * The inline `min-width: 0` on the track text did not help, and that is worth
 * understanding rather than working around: min-width on a flex ITEM governs
 * how far flex may shrink it, not the min-content size its block parent
 * reports upward to the grid. The grid asked the column, not the flex item.
 *
 * So the filters are a row across the top now rather than a column beside the
 * list, and the rows are a CSS grid whose text track is an explicit
 * `minmax(0, 1fr)` - a real zero minimum, which cannot be talked out of it by
 * anything inside. The list is full width, and there is no second column left
 * to blow out.
 */

/*
 * .pg-* rather than .mu-*: Projects, Lab and Music all filter a listing by a
 * handful of short labels, and there is no reason for that control to look
 * like three different things. Lab drew its filters as a glass panel of pill
 * buttons with inline onmouseover handlers; Projects drew them as .filter-btn
 * from projects-page.css. Both are this now.
 */
.pg-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 10px;
    align-items: center;
    margin: 0 0 10px;
    padding: 0;
    list-style: none;
}

.pg-filters + .pg-filters {
    margin-top: 2px;
}

.pg-filter-label {
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.4;
    margin-right: 6px;
    /* Keeps the label from being pulled taller than the pills beside it. */
    align-self: center;
}

.pg-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 6px 12px;
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    border-radius: 999px;
    background: transparent;
    color: var(--re-text-color);
    text-decoration: none;
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    opacity: 0.72;
    transition: border-color 0.16s ease, background 0.16s ease, opacity 0.16s ease;
    white-space: nowrap;
}

.pg-chip:hover {
    opacity: 1;
    border-color: var(--re-border-strong);
    background: var(--re-surface-raised);
    color: var(--re-text-color);
}

.pg-chip[aria-current="true"] {
    opacity: 1;
    border-color: rgba(186, 158, 187, 0.55);
    background: rgba(186, 158, 187, 0.16);
}

/* The count rides inside the pill, dimmer than the name. */
/*
 * The count inside a filter chip.
 *
 * It was 0.92em, which against the chip's 8px came out at 7.36px - Silkscreen
 * resampled to 92% of the size it is drawn at, which is exactly the blur the
 * pixel grid is meant to prevent. It reads as smaller than the label anyway,
 * because it is dimmed to half opacity.
 *
 * The em is the trap: this element inherits the UI face rather than asking
 * for it, so silkscreen-pixel-grid.css never sees it and cannot correct it.
 * Anything inheriting Silkscreen has to state 8px outright.
 */
.pg-chip-n {
    font-size: var(--re-size-ui, 8px);
    opacity: 0.5;
    font-variant-numeric: tabular-nums;
}

/* --- grouped ------------------------------------------------------- */

/*
 * A group heading already draws a rule under itself (.page-section-head has a
 * bottom border), and the list draws one over itself. Together they made a
 * double line under every heading.
 */
/*
 * Under a "CPS-2" heading, every row saying CPS-2 is noise - the heading has
 * already said it. The badge stays when grouping by album, where the system is
 * the one thing the heading does not tell you.
 */
.mu-group-system .mu-system {
    display: none;
}

.mu-group .mu-list {
    border-top: none;
}

/* Tighter than a full page section: these are headings within one list, not
   separate parts of the page. */
.mu-group {
    margin-bottom: clamp(28px, 4vw, 48px);
}

.mu-group .page-section-head {
    margin-bottom: 0;
    padding-bottom: 10px;
}

/* --- the list ------------------------------------------------------ */

.mu-list {
    list-style: none;
    margin: 0;
    padding: 0;
    border-top: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

/*
 * The row. Five explicit tracks; the third is the only flexible one and its
 * minimum is a literal 0 - see the note at the top of this section for why
 * that is the whole fix.
 */
.mu-track {
    display: grid;
    /* index, art, text, system, duration, playlist button */
    grid-template-columns: 30px 52px minmax(0, 1fr) auto auto auto;
    align-items: center;
    gap: 16px;
    padding: 11px 10px;
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    cursor: pointer;
    transition: background 0.16s ease;
}

.mu-track:hover,
.mu-track:focus-visible {
    background: var(--re-surface-raised);
    outline: none;
}

.mu-track.is-playing {
    background: rgba(186, 158, 187, 0.1);
}

/* --- index / play --------------------------------------------------- */

.mu-index {
    position: relative;
    text-align: center;
    font-family: var(--re-font-ui);
    font-size: 0.58rem;
    color: var(--re-text-color);
    opacity: 0.4;
    font-variant-numeric: tabular-nums;
}

/*
 * Number and play icon are stacked rather than swapped with display, so the
 * cell never changes width on hover and the row does not twitch.
 */
.mu-index i {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1rem;
    color: var(--re-brand-200, #ba9ebb);
    opacity: 0;
    transition: opacity 0.16s ease;
}

.mu-track:hover .mu-index span { opacity: 0; }
.mu-track:hover .mu-index i    { opacity: 1; }
.mu-track:hover .mu-index      { opacity: 1; }

/* --- art ------------------------------------------------------------ */

.mu-art {
    width: 52px;
    height: 52px;
    border-radius: var(--re-radius-sm, 6px);
    object-fit: cover;
    background: var(--re-surface-raised);
    border: 1px solid var(--re-border);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--re-brand-300);
    font-size: 1.1rem;
}

/* --- text ----------------------------------------------------------- */

/*
 * min-width: 0 again here. It is doing a different job from the grid track:
 * this is what lets the ellipsis happen, by allowing the block to be narrower
 * than the nowrap line inside it.
 */
.mu-text {
    min-width: 0;
}

.mu-title {
    font-family: var(--re-font-body);
    font-size: 0.94rem;
    font-weight: 600;
    color: var(--re-text-color);
    margin: 0 0 3px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mu-by {
    font-family: var(--re-font-body);
    font-size: 0.8rem;
    color: var(--re-text-color);
    opacity: 0.55;
    margin: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- system badge --------------------------------------------------- */

/*
 * Always rendered, even with nothing in it. The row is a grid with a fixed
 * number of tracks, so a row that skipped this cell pushed its duration
 * onto a second line - which is what happened on mobile, where the artwork
 * cell is hidden and the counts stopped lining up. :empty takes the pill
 * off so an empty cell is invisible rather than a stray outline.
 */
.mu-system:empty {
    border-color: transparent;
    padding: 0;
}

.mu-system {
    font-family: var(--re-font-ui);
    font-size: 0.5rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.55;
    padding: 4px 9px;
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    border-radius: 999px;
    white-space: nowrap;
}

.mu-duration {
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    color: var(--re-text-color);
    opacity: 0.4;
    font-variant-numeric: tabular-nums;
    text-align: right;
    min-width: 34px;
}

/* --- empty ---------------------------------------------------------- */

.pg-empty {
    text-align: center;
    padding: clamp(48px, 9vw, 90px) 20px;
    border: 1px dashed var(--page-rule, rgba(130, 107, 125, 0.22));
    border-radius: var(--re-radius-lg, 14px);
    color: var(--re-text-color);
}

.pg-empty i {
    font-size: 2.4rem;
    opacity: 0.35;
    display: block;
    margin-bottom: 14px;
}

.pg-empty p {
    font-family: var(--re-font-body);
    opacity: 0.6;
    margin: 0 0 20px;
}

/* --- narrow --------------------------------------------------------- */

@media (max-width: 720px) {
    /*
     * Art and the system badge go. On a 375px screen the badge alone was
     * taking a third of the row, and the cover art on these is a stock note
     * glyph for all but a handful of tracks - it is not carrying information
     * worth the width.
     */
    .mu-track {
        /* index, text, duration, playlist button */
        grid-template-columns: 26px minmax(0, 1fr) auto auto;
        gap: 12px;
        padding: 10px 4px;
    }

    /* The controls survive the narrow layout; the badge and artwork do not. */
    .mu-track.has-admin {
        grid-template-columns: 26px minmax(0, 1fr) auto auto auto;
    }

    .mu-admin { gap: 2px; }

    .mu-art,
    .mu-system {
        display: none;
    }

    .mu-title { font-size: 0.9rem; }
    .mu-by    { font-size: 0.75rem; }
}

/* ------------------------------------------------------------------ *
 * About
 * ------------------------------------------------------------------ */

/*
 * The last bespoke page shell on the site.
 *
 * About had its own hero - a Bootstrap .row with the avatar in a col-md-4 and
 * an <h1> in a col-md-8 - which is not what any other page does, so the page
 * announced itself in a different voice from Home, Music and the rest. It also
 * alternated its sections between a "contained" panel and an "open" one, which
 * read as two different designs interleaved rather than one rhythm.
 *
 * It now opens with the shared .page-head like every other inner page, and
 * every section below is a .page-section with a .page-section-head - the same
 * title-plus-hairline the Music page uses. What is left here is only the parts
 * About genuinely has and no other page does: the profile row, the skill
 * meters, the experience list and the game shelves.
 *
 * New .ab-* names on purpose. about-page.css still loads (21st of 32) and
 * carries the old .profile-*, .section-* and .skills-grid rules; reusing those
 * names would have meant winning a specificity argument on every declaration.
 * A new namespace simply does not have the argument.
 */

/* --- identity block -------------------------------------------------- */

/*
 * Who is writing, and what they do, side by side.
 *
 * This was a full-width row holding only the avatar, the name and five icons -
 * 141px of page carrying about a line of information - with the paragraph that
 * belonged next to it stranded in its own section further down. The two halves
 * are one grid now, which is both shorter and the correct pairing.
 *
 * The bio column is the wider one: it is the part with something to say.
 */
.ab-identity {
    /*
     * Stacked, not a two-column grid. The grid put the bio in a second column
     * whose left edge sat ~370px in - out of line with the masthead lead and
     * every section's prose, which all begin at the page's left margin. That
     * made the identity read as detached and floated the name and the bio at
     * different heights. Stacking keeps the avatar, name, links and bio on the
     * one left edge, in reading order, and the block lines up with the rest of
     * the page.
     */
    padding: 0 0 clamp(24px, 3.5vw, 36px);
    margin-bottom: clamp(28px, 4vw, 44px);
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

/* No bio in the database - the left column takes the full width rather than
   leaving a hole where the paragraph would have been. */
.ab-identity.is-alone {
    grid-template-columns: minmax(0, 1fr);
}

.ab-who {
    display: flex;
    align-items: center;
    /* Wraps so the links get a full-width row under the avatar and the name
       rather than the strip left over beside them. */
    flex-wrap: wrap;
    gap: clamp(14px, 2vw, 20px);
}

/* min-width: 0 so a long tagline ellipsises instead of widening the column. */
.ab-who-text {
    min-width: 0;
}

.ab-bio {
    /* Full-width paragraph beneath the identity row, not a second column. */
    margin-top: clamp(18px, 2.4vw, 26px);
}

.ab-avatar {
    width: clamp(88px, 10vw, 116px);
    height: clamp(88px, 10vw, 116px);
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
    border: 1px solid var(--re-border-strong);
    background: var(--re-surface-raised);
}

.ab-profile-name {
    font-family: var(--re-font-display) !important;
    font-size: clamp(1.1rem, 1.9vw, 1.4rem);
    line-height: 1.1;
    margin: 0 0 6px;
    color: var(--re-text-color);
}

.ab-profile-tagline {
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.45;
    margin: 0;
}

.ab-social {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    /* Own row inside .ab-who. */
    width: 100%;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ab-social a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    color: var(--re-text-color);
    opacity: 0.6;
    font-size: 0.95rem;
    text-decoration: none;
    transition: opacity 0.16s ease, border-color 0.16s ease, background 0.16s ease;
}

.ab-social a:hover {
    opacity: 1;
    border-color: rgba(186, 158, 187, 0.5);
    background: rgba(186, 158, 187, 0.14);
    color: var(--re-text-color);
}

/* --- prose ---------------------------------------------------------- */

.ab-prose {
    font-family: var(--re-font-body);
    font-size: clamp(0.95rem, 1.2vw, 1.05rem);
    line-height: 1.65;
    color: var(--re-text-color);
    opacity: 0.78;
    max-width: 68ch;
    margin: 0;
}

/* --- game shelves ---------------------------------------------------- */

/*
 * Two rails inside one section, where there used to be two sections.
 *
 * .ab-shelf-title is a rank below .page-section-title and has to look it, so
 * it takes the display face at roughly half the size rather than becoming
 * another heading competing with the one above it.
 */
.ab-shelf + .ab-shelf {
    margin-top: clamp(28px, 3.5vw, 42px);
    padding-top: clamp(24px, 3vw, 34px);
    border-top: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

.ab-shelf-title {
    font-family: var(--re-font-display) !important;
    font-size: clamp(0.95rem, 1.5vw, 1.15rem);
    line-height: 1.1;
    letter-spacing: 0.02em;
    color: var(--re-text-color);
    opacity: 0.8;
    margin: 0 0 14px;
}

/* --- skills and experience (shared with Resume) --------------------- */

/*
 * .cv-* rather than .ab-*: About and Resume render the same two tables
 * from the same model, so a job or a skill looks identical on both. The
 * prefix says shared, not "belongs to About".
 */

.cv-skills {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: clamp(24px, 3vw, 40px);
    /*
     * Each category is as tall as its own contents.
     *
     * A grid row stretches its cells to the tallest by default, so
     * Environment Art - two skills - was padded out to the 220px of the
     * four-skill categories beside it and trailed a hundred pixels of empty
     * space under its last rail. The categories are lists, not cards; there
     * is nothing to line the bottom edges up for.
     */
    align-items: start;
    list-style: none;
    margin: 0;
    padding: 0;
}

.cv-skill-cat > h3 {
    font-family: var(--re-font-ui) !important;
    font-size: 0.58rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.45;
    margin: 0 0 14px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

.cv-skill-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.cv-skill {
    margin-bottom: 13px;
}

.cv-skill-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 6px;
}

.cv-skill-name {
    font-family: var(--re-font-body);
    font-size: 0.86rem;
    color: var(--re-text-color);
    opacity: 0.85;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cv-skill-pct {
    font-family: var(--re-font-ui);
    font-size: 0.5rem;
    color: var(--re-text-color);
    opacity: 0.4;
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
}

/*
 * Deliberately the same rail as the player's progress and volume bars in
 * site-chrome.css: 4px tall, 2px radius, brand-400 at 0.28 behind a brand-200
 * fill. A proficiency meter and a seek bar are both "how far along this is",
 * and there is no reason for the site to draw that idea two ways.
 */
.cv-skill-track {
    height: 4px;
    border-radius: 2px;
    background: rgba(var(--re-brand-400-rgb), 0.28);
    overflow: hidden;
}

.cv-skill-fill {
    height: 100%;
    border-radius: 2px;
    background: var(--re-brand-200);
}

/* --- experience ----------------------------------------------------- */

.cv-exp {
    list-style: none;
    margin: 0;
    padding: 0;
    border-top: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

/*
 * One rule between a heading and its list, not two.
 *
 * .page-section-head has a border-bottom and these lists have a border-top,
 * which is a double line the moment there is any space between them. It went
 * unnoticed for as long as there was none: --page-gap did not resolve outside
 * .page-head/.page-body, so the heading's margin-bottom computed to 0 and the
 * two borders landed on the same pixel row.
 *
 * Adjacent-sibling, not the bare class: a list that appears without a heading
 * above it still needs its own top edge.
 */
.page-section-head + .cv-exp,
.page-section-head + .rs-list {
    border-top: none;
}

.cv-exp > li {
    display: grid;
    /* The date rail is its own column so every role's title starts on the same
       line, however long the dates are. */
    grid-template-columns: minmax(0, 150px) minmax(0, 1fr);
    gap: 6px clamp(18px, 2.5vw, 34px);
    padding: clamp(16px, 2vw, 22px) 0;
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

.cv-exp-when {
    font-family: var(--re-font-ui);
    font-size: 0.54rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.42;
    padding-top: 4px;
}

.cv-exp-role {
    font-family: var(--re-font-body);
    font-size: 1rem;
    font-weight: 600;
    color: var(--re-text-color);
    margin: 0 0 3px;
}

.cv-exp-where {
    font-family: var(--re-font-ui);
    font-size: 0.52rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    margin: 0 0 10px;
}

.cv-exp-desc {
    font-family: var(--re-font-body);
    font-size: 0.9rem;
    line-height: 1.6;
    color: var(--re-text-color);
    opacity: 0.68;
    margin: 0 0 12px;
}

.cv-exp-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 8px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.cv-exp-tags li {
    font-family: var(--re-font-ui);
    font-size: 0.48rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.55;
    padding: 4px 9px;
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    border-radius: 999px;
}

/* --- narrow --------------------------------------------------------- */

@media (max-width: 720px) {
    /*
     * One column: at this width the bio has nowhere to sit beside the avatar,
     * and 220px of name next to 100px of paragraph helps nobody.
     */
    .ab-identity {
        grid-template-columns: minmax(0, 1fr);
        gap: 20px;
    }

    .ab-bio {
        padding-top: 0;
    }

    /* Dates above the role rather than beside it - a 150px rail is most of a
       375px screen. */
    .cv-exp > li {
        grid-template-columns: minmax(0, 1fr);
        gap: 4px;
    }

    .cv-exp-when {
        padding-top: 0;
    }
}

/* ------------------------------------------------------------------ *
 * Resume
 * ------------------------------------------------------------------ */

/*
 * Resume already had the shared masthead and section heads. What it still had
 * underneath was the old card language: every entry a `.glass .card` panel
 * with about a dozen inline style attributes each, dates and tags as
 * `.badge.glass-light` in 8px Silkscreen, and every icon coloured
 * var(--bs-link-hover-color) - a Bootstrap link colour, not one of this site's
 * brand tokens, which is why the accents on this page were a different hue
 * from every other page.
 *
 * The entries are hairline-separated rows now, like About and Music. Nothing
 * on this page is a panel any more.
 *
 * Experience and Skills are NOT here: they are .cv-* and shared with About,
 * because both pages render the same two tables from the same model and there
 * is no reason for a job to look like one thing on one page and another
 * thing on the next.
 */

.rs-contact {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 20px;
    padding: 0 0 clamp(20px, 3vw, 30px);
    margin-bottom: clamp(28px, 4vw, 44px);
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.rs-contact a,
.rs-contact > span {
    color: var(--re-text-color);
    opacity: 0.6;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    transition: opacity 0.16s ease;
}

.rs-contact a:hover {
    opacity: 1;
    color: var(--re-text-color);
}

/* --- education ------------------------------------------------------ */

.rs-list {
    list-style: none;
    margin: 0;
    padding: 0;
    border-top: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

.rs-list > li {
    display: grid;
    grid-template-columns: minmax(0, 150px) minmax(0, 1fr);
    gap: 6px clamp(18px, 2.5vw, 34px);
    padding: clamp(16px, 2vw, 22px) 0;
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

.rs-when {
    font-family: var(--re-font-ui);
    font-size: 0.54rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.42;
    padding-top: 4px;
}

.rs-title {
    font-family: var(--re-font-body);
    font-size: 1rem;
    font-weight: 600;
    color: var(--re-text-color);
    margin: 0 0 3px;
}

.rs-where {
    font-family: var(--re-font-ui);
    font-size: 0.52rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    margin: 0 0 10px;
}

.rs-body {
    font-family: var(--re-font-body);
    font-size: 0.9rem;
    line-height: 1.6;
    color: var(--re-text-color);
    opacity: 0.68;
    margin: 0 0 12px;
}

/*
 * Key achievements, and the quote in a recommendation. A rule down the left
 * rather than a tinted box: the old version stacked a panel inside a panel,
 * and a single line is enough to say "this is set apart".
 */
.rs-aside {
    border-left: 2px solid rgba(186, 158, 187, 0.45);
    padding: 2px 0 2px 16px;
    margin: 0 0 14px;
}

.rs-aside-label {
    display: block;
    font-family: var(--re-font-ui);
    font-size: 0.5rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.45;
    margin-bottom: 6px;
}

.rs-aside p {
    font-family: var(--re-font-body);
    font-size: 0.88rem;
    line-height: 1.6;
    color: var(--re-text-color);
    opacity: 0.7;
    margin: 0;
}

/* --- recommendations ------------------------------------------------ */

.rs-recs {
    display: grid;
    /*
     * auto-FIT, not auto-fill, and the difference is the whole layout: fill
     * keeps the tracks nothing lands in, so three recommendations sat in the
     * first three of four columns with a 300px hole on the right. fit
     * collapses the empty track and the three share the row.
     *
     * The quote's measure is capped below rather than here, so a page with
     * one recommendation gets a readable column instead of a single line of
     * italic running the full 1320px.
     */
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: clamp(24px, 3vw, 38px);
    /* Same reason as the skills grid: these are quotes of different lengths
       with no card behind them, so a short one should end where it ends. */
    align-items: start;
    list-style: none;
    margin: 0;
    padding: 0;
}

.rs-rec-quote {
    font-family: var(--re-font-body);
    font-size: 0.92rem;
    line-height: 1.65;
    font-style: italic;
    color: var(--re-text-color);
    opacity: 0.78;
    border-left: 2px solid rgba(186, 158, 187, 0.45);
    padding: 2px 0 2px 16px;
    margin: 0 0 16px;
    /* The grid track can be the full width when there is one recommendation;
       the line length must not follow it. */
    max-width: 62ch;
}

.rs-rec-who {
    font-family: var(--re-font-body);
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--re-text-color);
    margin: 0 0 3px;
}

.rs-rec-role {
    font-family: var(--re-font-ui);
    font-size: 0.5rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    margin: 0 0 10px;
}

/* --- narrow --------------------------------------------------------- */

@media (max-width: 720px) {
    /* A 150px date rail is most of a 375px screen; dates go above instead. */
    .rs-list > li {
        grid-template-columns: minmax(0, 1fr);
        gap: 4px;
    }

    .rs-when {
        padding-top: 0;
    }
}

/* --- lab ------------------------------------------------------------ */

/*
 * Lab reuses the Portfolio card wholesale - it is the same shape of thing, a
 * titled project with a description, tags and links. Only what Lab has and
 * Portfolio does not lives here.
 */

.lb-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 8px;
    margin-bottom: 10px;
}

/*
 * Type and status ride above the title. They used to be absolutely positioned
 * over the cover image, which meant a card without one showed neither - and
 * most Lab entries have no cover image yet.
 */
.lb-tag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--re-font-ui);
    font-size: 0.48rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.55;
    padding: 4px 9px;
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    border-radius: 999px;
}

/*
 * The dot is the only place a status keeps its own colour. The old cards set
 * the whole label to it - four saturated hues (#28a745, #007bff, #dc3545...)
 * against a mauve page, none of them from the palette. A 6px dot says the
 * same thing without pulling the page apart.
 */
.lb-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex: 0 0 auto;
    display: inline-block;
}

.lb-updated {
    font-family: var(--re-font-ui);
    font-size: 0.48rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.35;
    margin: 0 0 14px;
}

/* --- search and sort, beside the filter pills ----------------------- */

/*
 * The Projects page needs two controls the other listings do not: a search box
 * and a sort order. They sit on one line under the pills rather than in a
 * panel of their own, and they are built to read as part of the same row -
 * same height, same hairline border, same uppercase UI face.
 */

.pg-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
    margin: 0 0 clamp(24px, 3vw, 36px);
}

.pg-search {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    flex: 1 1 240px;
    /* min-width: 0 so the field can shrink below its intrinsic size instead of
       pushing the sort and buttons onto their own line at every width. */
    min-width: 0;
    padding: 6px 14px;
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    border-radius: 999px;
    color: var(--re-text-color);
    opacity: 0.72;
    transition: border-color 0.16s ease, opacity 0.16s ease;
}

.pg-search:focus-within {
    opacity: 1;
    border-color: rgba(186, 158, 187, 0.55);
}

.pg-search input {
    flex: 1 1 auto;
    min-width: 0;
    background: none;
    border: none;
    outline: none;
    color: var(--re-text-color);
    font-family: var(--re-font-body);
    font-size: 0.85rem;
}

.pg-search input::placeholder {
    color: var(--re-text-color);
    opacity: 0.4;
}

/*
 * select.pg-select, not .pg-select. projects-page.css carries
 * select[name="sort"] - a type plus an attribute, which is (0,1,1) against
 * a bare class's (0,1,0) and wins however late this file loads. Adding the
 * element name matches its weight, and loading later then settles it.
 */
select.pg-select {
    /* Same box as .pg-search beside it: one height, one pill, one border for
       the whole row. It was 28px against the field's 44 and read as a
       different class of control. */
    height: 44px;
    padding: 0 38px 0 18px;
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    border-radius: 999px;
    /*
     * `background: transparent` alone was not enough. While appearance is
     * `auto` the browser still paints its own control chrome under whatever
     * background is set - on Windows that is a pale slab, which is exactly
     * how this looked in a dark row. Turning appearance off is what stops it.
     */
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    background-color: transparent;
    /* appearance:none also removes the native arrow, so here is one. White
       rather than currentColor - a data: URI cannot read the cascade - and
       the element's own opacity dims it along with the text. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23ffffff' stroke-width='1.4' stroke-linecap='square'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 16px center;
    background-size: 10px 6px;
    color: var(--re-text-color);
    /*
     * !important, and only because japanese-support.css sets
     * `input[type=text], input[type=email], textarea, select` to the body
     * face with one. That rule exists so a Japanese visitor gets a face that
     * can draw kana in a long-form field, which is right for a text input and
     * wrong for a four-option sort control. An !important declaration is
     * beaten only by another !important, whatever the specificity and
     * whatever the load order - so this is the only way to opt out short of
     * carving the selector out of that rule, and :not() chains there have
     * broken unrelated Silkscreen selectors before.
     */
    font-family: var(--re-font-ui) !important;
    font-size: 0.56rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    opacity: 0.72;
    cursor: pointer;
}

select.pg-select:hover,
select.pg-select:focus {
    opacity: 1;
    border-color: rgba(186, 158, 187, 0.55);
    outline: none;
}

/*
 * The toolbar's own buttons, at the height of the field and the sort.
 *
 * .pg-chip is shared with the type filters above, which are small pills and
 * should stay small; only the ones inside the tools row grow, so search,
 * sort, Search and Clear form a single line of controls instead of a tall
 * field with two little tabs beside it.
 */
.pg-tools .pg-chip {
    height: 44px;
    padding: 0 20px;
}

/*
 * The Search and Clear controls, kept as outlines.
 *
 * The site's button standard carries
 *
 *   button[type="submit"]:not(.btn-secondary-standard)
 *                        :not(.btn-danger-standard)
 *                        :not(.adm-btn)
 *
 * with !important on background, border and border-radius, which turned the
 * Search button into a filled grey slab with a 10px radius in a row of
 * pills. !important on this side is not enough on its own: when both sides
 * are important the more specific one still wins, and every :not(.class)
 * adds a class, so that selector is (0,4,1) against a plain
 * `.pg-tools .pg-chip` at (0,2,0).
 *
 * The repeated classes below are the cheapest way to (0,5,1). They look like
 * a typo and are not - removing one silently hands the row back to the
 * button standard, so they are load-bearing.
 */
.pg-tools.pg-tools button.pg-chip.pg-chip {
    border-radius: 999px !important;
    background: transparent !important;
    background-image: none !important;
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22)) !important;
    box-shadow: none !important;
    color: var(--re-text-color) !important;
}

.pg-tools.pg-tools button.pg-chip.pg-chip:hover {
    border-color: rgba(186, 158, 187, 0.55) !important;
}

/*
 * The field is the wrapper, not the input.
 *
 * standardized-input-heights.css gives every text input a 44px height and
 * 12px/16px padding with !important - correct for a form, wrong inside a
 * bordered pill that already carries the padding, where it stacked up to a
 * 58px field beside 44px controls. The input goes back to filling its
 * parent and .pg-search sets the height for the pair.
 */
.pg-search {
    height: 44px;
}

.pg-search input {
    height: auto !important;
    padding: 0 !important;
    /* The global rule raises this to 16px below 768px to stop iOS zooming the
       page on focus. That still matters, so only the desktop size is pinned
       back; the mobile bump is left alone in the media query below. */
    font-size: 0.85rem !important;
}

@media (max-width: 767px) {
    .pg-search input {
        /* Anything under 16px makes Safari zoom the viewport on focus and
           never zoom back out. */
        font-size: 16px !important;
    }
}

/* The dropdown itself is drawn by the OS, which does not inherit the page. */
select.pg-select option {
    background: #1a141c;
    color: #d8c8d6;
}

/* A <button> needs the reset a link does not. */
button.pg-chip {
    background: transparent;
    font: inherit;
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
}

/* ------------------------------------------------------------------ *
 * Bulk music upload (admin)
 * ------------------------------------------------------------------ */

.bulk-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: clamp(34px, 6vw, 64px) 20px;
    border: 1px dashed var(--page-rule, rgba(130, 107, 125, 0.28));
    border-radius: var(--re-radius-lg, 14px);
    text-align: center;
    transition: border-color 0.16s ease, background 0.16s ease;
}

/* Only while something is actually over it - a permanent highlight reads as
   "this is busy" when nothing is happening. */
.bulk-drop.is-over {
    border-color: rgba(186, 158, 187, 0.7);
    background: rgba(186, 158, 187, 0.09);
}

.bulk-drop > i {
    font-size: 2rem;
    opacity: 0.35;
}

.bulk-drop-lead {
    font-family: var(--re-font-body);
    font-size: 0.98rem;
    color: var(--re-text-color);
    opacity: 0.8;
    margin: 0;
}

.bulk-drop-note {
    font-family: var(--re-font-ui);
    font-size: 0.5rem;
    letter-spacing: 0.12em;
    color: var(--re-text-color);
    opacity: 0.35;
    margin: 0;
}

.bulk-hint,
.bulk-options {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 20px;
    margin: 16px 0 0;
    font-family: var(--re-font-body);
    font-size: 0.84rem;
    color: var(--re-text-color);
    opacity: 0.62;
}

.bulk-hint code {
    font-size: 0.92em;
    padding: 1px 5px;
    border-radius: 4px;
    background: var(--re-surface-hover);
}

.bulk-options label {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
}

/* --- progress ------------------------------------------------------- */

.bulk-run {
    margin: 22px 0 6px;
}

/* Same 4px rail as the player and the skill meters. */
.bulk-bar {
    height: 4px;
    border-radius: 2px;
    background: rgba(var(--re-brand-400-rgb), 0.28);
    overflow: hidden;
}

.bulk-bar-fill {
    height: 100%;
    width: 0;
    border-radius: 2px;
    background: var(--re-brand-200);
    transition: width 0.2s ease;
}

.bulk-status {
    font-family: var(--re-font-ui);
    font-size: 0.56rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.5;
    margin: 10px 0 0;
}

/* --- results -------------------------------------------------------- */

.bulk-results {
    list-style: none;
    margin: 18px 0 40px;
    padding: 0;
}

.bulk-result {
    display: grid;
    /* minmax(0,...) on both, so a long filename ellipsises instead of pushing
       the detail column off the row. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
    gap: 8px 18px;
    padding: 9px 0 9px 12px;
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.18));
    border-left: 2px solid transparent;
    font-family: var(--re-font-body);
    font-size: 0.85rem;
}

.bulk-result-name {
    color: var(--re-text-color);
    opacity: 0.8;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bulk-result-detail {
    color: var(--re-text-color);
    opacity: 0.55;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The left rule is the whole status signal: four states, no icons to decode. */
.bulk-result.is-ok      { border-left-color: rgba(95, 191, 114, 0.75); }
.bulk-result.is-library { border-left-color: rgba(186, 158, 187, 0.75); }
.bulk-result.is-skip    { border-left-color: rgba(154, 146, 164, 0.6); }
.bulk-result.is-error   { border-left-color: rgba(217, 112, 112, 0.85); }
.bulk-result.is-error .bulk-result-detail { opacity: 0.85; }

@media (max-width: 720px) {
    .bulk-result {
        grid-template-columns: minmax(0, 1fr);
        gap: 2px;
    }
}


/* ------------------------------------------------------------------ *
 * Music page: admin quick actions
 * ------------------------------------------------------------------ */

/*
 * Rendered only for an admin, so the extra column only exists then - hence
 * the .has-admin class rather than :has(.mu-admin). A visitor's grid is
 * untouched, and this does not depend on :has() support to lay out correctly.
 */
.mu-track.has-admin {
    grid-template-columns: 30px 52px minmax(0, 1fr) auto auto auto auto;
}

.mu-admin {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.mu-admin-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 30px: smaller than the 44px a thumb needs, because these sit inside a
       dense list on a screen with a mouse, and larger targets here would push
       the track title's column in. Still comfortably clickable. */
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--re-radius-sm);
    background: none;
    color: var(--re-text-color);
    opacity: 0.35;
    cursor: pointer;
    text-decoration: none;
    font-size: 13px;
    line-height: 1;
    transition: opacity var(--re-transition), background var(--re-transition),
                border-color var(--re-transition), color var(--re-transition);
}

/*
 * Faint until the row is hovered. A list of 368 tracks with three bright
 * icons on every line is a control panel, not a list of music - they should
 * be there when you reach for them and quiet when you are not.
 */
.mu-track:hover .mu-admin-btn,
.mu-admin-btn:focus-visible {
    opacity: 0.75;
}

.mu-admin-btn:hover {
    opacity: 1;
    background: var(--re-surface-hover);
    border-color: var(--re-border);
}

/* Featured: on is the loud state. */
.mu-admin-btn.is-on {
    opacity: 0.95;
    color: var(--re-brand-200);
}

/* Draft: the eye is struck through and stays visible, because it is telling
   you something is wrong rather than offering an action. */
.mu-admin-btn.is-off {
    opacity: 0.9;
    color: #e0a13a;
}

.mu-admin-btn.is-error {
    color: #e06a5a;
    border-color: #e06a5a;
    opacity: 1;
}

.mu-admin-btn:disabled { cursor: progress; }

/* ------------------------------------------------------------------ *
 * Admin: default playlist order
 * ------------------------------------------------------------------ */

.mq-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 20px;
    margin-bottom: 20px;
    flex-wrap: wrap;
}

.mq-title {
    color: var(--re-brand-200);
    font-size: 24px;
    margin: 0 0 6px;
    font-family: 'Albert Sans', sans-serif;
}

.mq-sub {
    margin: 0;
    max-width: 46ch;
    color: var(--re-text-color);
    opacity: 0.7;
    font-size: 0.8rem;
}

.mq-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 18px;
    border: 1px solid rgba(130, 107, 125, 0.45);
    border-radius: var(--re-radius-md);
    background: rgba(130, 107, 125, 0.28);
    color: var(--re-text-color);
    text-decoration: none;
    /* No font-size: silkscreen-pixel-grid.css settles every Silkscreen run at
       8px, so a number here would only be a lie about what renders. Tracking
       is a whole pixel - 0.5px puts the next glyph off the grid. */
    font-family: 'Silkscreen', serif;
    text-transform: uppercase;
    letter-spacing: 1px;
    cursor: pointer;
}

.mq-btn:hover { background: rgba(130, 107, 125, 0.45); }

.mq-btn-go {
    background: #5d4b60;
    border-color: #5d4b60;
    color: #fff;
}

/* Nothing has moved yet, so there is nothing to save. */
.mq-btn-go:disabled {
    opacity: 0.35;
    cursor: default;
}

.mq-bar {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 12px 0;
    margin-bottom: 8px;
    background: var(--re-bg, #14121a);
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
}

.mq-count {
    font-family: var(--re-font-ui);
    text-transform: uppercase;
    letter-spacing: 1px;
    opacity: 0.6;
}

.mq-msg {
    padding: 12px 16px;
    border-radius: var(--re-radius-md);
    border: 1px solid transparent;
    font-size: 0.85rem;
}

.mq-msg-success {
    background: rgba(95, 191, 114, 0.12);
    border-color: rgba(95, 191, 114, 0.5);
}

.mq-msg-error {
    background: rgba(224, 106, 90, 0.12);
    border-color: rgba(224, 106, 90, 0.5);
}

.mq-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.mq-row {
    display: grid;
    /* grip, number, art, text, arrows */
    grid-template-columns: 24px 34px 40px minmax(0, 1fr) auto;
    align-items: center;
    gap: 14px;
    padding: 9px 10px;
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    background: var(--re-bg, transparent);
}

.mq-row:hover { background: var(--re-surface-raised); }

/*
 * The row being dragged stays in the list and is dimmed, rather than being
 * lifted out of it. The gap it would leave is the whole point: the list
 * reflows live as the pointer moves, so what you see during the drag is what
 * you get when you let go.
 */
.mq-row.is-dragging {
    opacity: 0.4;
    background: rgba(186, 158, 187, 0.14);
}

.mq-grip {
    cursor: grab;
    text-align: center;
    opacity: 0.4;
    font-size: 15px;
}

.mq-row:hover .mq-grip { opacity: 0.8; }
.mq-grip:active { cursor: grabbing; }

.mq-pos {
    font-family: var(--re-font-ui);
    text-align: right;
    opacity: 0.5;
    font-variant-numeric: tabular-nums;
}

.mq-art {
    width: 40px;
    height: 40px;
    object-fit: cover;
    border-radius: var(--re-radius-sm);
    background: var(--re-surface-raised);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 0.85;
}

.mq-text  { min-width: 0; display: grid; gap: 2px; }
.mq-name  { font-size: 0.9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mq-by    { font-size: 0.72rem; opacity: 0.55; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.mq-moves { display: inline-flex; gap: 2px; }

.mq-move {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 34px: these are the way this is done on a phone, where dragging inside
       a scrolling page is a fight, so they get a real target. */
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--re-radius-sm);
    background: none;
    color: var(--re-text-color);
    opacity: 0.4;
    cursor: pointer;
    font-size: 13px;
}

.mq-row:hover .mq-move,
.mq-move:focus-visible { opacity: 0.85; }

.mq-move:hover {
    opacity: 1;
    background: var(--re-surface-hover);
    border-color: var(--re-border);
}

.mq-move:disabled {
    opacity: 0.12;
    cursor: default;
    background: none;
    border-color: transparent;
}

@media (hover: none) {
    .mq-grip,
    .mq-move { opacity: 0.7; }
}

@media (max-width: 720px) {
    .mq-row {
        /* The artwork goes; the number, the title and the arrows are what
           this page is for. */
        grid-template-columns: 20px 28px minmax(0, 1fr) auto;
        gap: 10px;
    }
    .mq-art { display: none; }
}

/* ------------------------------------------------------------------ *
 * Music page: add to / remove from the playlist
 * ------------------------------------------------------------------ */

/*
 * The only control on a row that changes the queue.
 *
 * Pressing the row plays the track and leaves the playlist alone; this is how
 * a track gets into it, and how it comes back out. Both in one button,
 * because the useful thing to show is the state - a plus when the track is
 * not in the list, a tick when it is - rather than two icons the reader has
 * to choose between.
 *
 * 30px to match .mu-admin-btn beside it. Dense list, pointer device; see the
 * note there.
 */
.mu-queue-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--re-radius-sm);
    background: none;
    color: var(--re-text-color);
    opacity: 0.3;
    cursor: pointer;
    font-size: 13px;
    line-height: 1;
    transition: opacity var(--re-transition), background var(--re-transition),
                border-color var(--re-transition), color var(--re-transition);
}

.mu-track:hover .mu-queue-btn,
.mu-queue-btn:focus-visible {
    opacity: 0.75;
}

.mu-queue-btn:hover {
    opacity: 1;
    background: var(--re-surface-hover);
    border-color: var(--re-border);
}

/*
 * In the playlist. Loud, and loud whether or not the row is hovered - this is
 * the one piece of information the button exists to carry, and hiding it
 * until the pointer arrives would mean scanning the list by hovering it.
 */
.mu-queue-btn.is-queued {
    opacity: 1;
    color: var(--re-brand-200);
    border-color: rgba(186, 158, 187, 0.45);
    background: rgba(186, 158, 187, 0.12);
}

.mu-queue-btn.is-queued:hover {
    color: #ffb4a8;
    border-color: rgba(224, 106, 90, 0.55);
    background: rgba(224, 106, 90, 0.16);
}

/* A short pulse on the press, so the change registers on a long list where
   the button may be the only thing that moved. */
.mu-queue-btn.just-changed {
    animation: mu-queue-pop 0.32s ease;
}

@keyframes mu-queue-pop {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.28); }
    100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .mu-queue-btn.just-changed { animation: none; }
}

/*
 * Touch has no hover, so the plus is simply visible - faint, but there. On a
 * phone it is otherwise invisible until pressed, which is no use at all.
 */
@media (hover: none) {
    .mu-queue-btn { opacity: 0.5; }
}

/*
 * A drafted row, seen only by an admin. Dimmed and striped so it is obvious
 * at a glance which tracks the public cannot see, without removing it from
 * the list you are working in.
 */
.mu-track.is-draft {
    opacity: 0.62;
    background-image: repeating-linear-gradient(
        135deg,
        transparent 0 8px,
        rgba(224, 161, 58, 0.05) 8px 16px);
}

.mu-track.is-draft .mu-title::after {
    content: "DRAFT";
    display: inline-block;
    margin-left: 8px;
    padding: 1px 5px;
    border: 1px solid rgba(224, 161, 58, 0.5);
    border-radius: 3px;
    color: #e0a13a;
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    vertical-align: middle;
}

/* ------------------------------------------------------------------ *
 * Auth forms
 * ------------------------------------------------------------------ *
 *
 * Login, register, password reset. Replaces sixteen inline style attributes
 * on login.php, four hardcoded #826b7d values, and a pair of onfocus/onblur
 * handlers that set border colours from JavaScript - a focus ring is what
 * :focus is for.
 */

/*
 * The auth layout.
 *
 * Two columns on a desktop - the form, and whatever is useful beside it -
 * collapsing to one on anything narrow. This is the shape a detail page uses:
 * panels of the same material, each under its own Silkscreen label, filling
 * the measure rather than sitting in the middle of it.
 */
.auth-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: clamp(18px, 2.5vw, 32px);
    align-items: start;
    margin-bottom: clamp(40px, 6vw, 72px);
}

@media (max-width: 880px) {
    .auth-grid {
        /* One column. The aside becomes what follows the form, which is the
           order it is read in anyway. */
        grid-template-columns: minmax(0, 1fr);
    }
}

/*
 * A panel. Deliberately the same surface as .dt-panel and .dt-fact - same
 * ground, same hairline, same radius - so an auth page is made of the same
 * material as a project page rather than of its own thing.
 */
.auth-panel {
    display: flex;
    flex-direction: column;
    gap: 20px;
    padding: clamp(20px, 3vw, 30px);
    border: 1px solid var(--re-border, rgba(130, 107, 125, 0.22));
    border-radius: var(--re-radius-lg, 12px);
    background: rgba(0, 0, 0, 0.25);
}

/* The aside's items: a heading, a line of text, somewhere to go. */
.auth-aside-item {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.auth-aside-item + .auth-aside-item {
    padding-top: 20px;
    border-top: 1px solid var(--re-border, rgba(130, 107, 125, 0.22));
}

.auth-aside-text {
    margin: 0;
    font-family: var(--re-font-body);
    font-size: 0.86rem;
    line-height: 1.6;
    color: var(--re-text-color);
    opacity: 0.7;
}

/* A short list of what an account is actually for. Reads as facts, like the
   detail pages, rather than as marketing. */
.auth-points {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.auth-points li {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    font-family: var(--re-font-body);
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--re-text-color);
    opacity: 0.72;
}

.auth-points i {
    margin-top: 2px;
    opacity: 0.55;
    flex: 0 0 auto;
}

/* The button in an aside sits with its item rather than filling the panel. */
.auth-aside .pf-btn {
    align-self: flex-start;
}

.auth-wrap {
    /* A form nobody fills in twice does not want the full page measure. */
    max-width: 460px;
    margin: 0 auto clamp(40px, 6vw, 72px);
}

.auth-card {
    display: flex;
    flex-direction: column;
    gap: 20px;
    padding: clamp(22px, 4vw, 34px);
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    border-radius: var(--re-radius-md, 10px);
    background: rgba(0, 0, 0, 0.28);
}

.auth-field {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* The label row when something sits opposite the label - the forgot-password
   link. baseline, so the 8px pixel label and the body-face link sit on the
   same line rather than centring against each other. */
.auth-label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

/* No font-size: the pixel grid owns the size of every Silkscreen face.
   Registered in silkscreen-pixel-grid.css. */
.auth-label {
    font-family: var(--re-font-ui);
    text-transform: uppercase;
    color: var(--re-text-color);
    opacity: 0.55;
    margin: 0;
}

/* The small link that sits opposite a label - "Forgot password?".
   Named -link because .auth-aside is the panel beside the form. */
.auth-aside-link {
    font-family: var(--re-font-body);
    font-size: 0.78rem;
    color: var(--re-text-color);
    opacity: 0.6;
    text-decoration: none;
    transition: opacity 0.16s ease;
}

.auth-aside-link:hover {
    opacity: 1;
    color: var(--re-text-color);
}

.auth-input {
    width: 100%;
    padding: 12px 14px;
    border: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    border-radius: var(--re-radius-sm, 6px);
    background: rgba(0, 0, 0, 0.3);
    color: var(--re-text-color);
    font-family: var(--re-font-body);
    font-size: 0.95rem;
    transition: border-color 0.16s ease, background 0.16s ease;
}

.auth-input::placeholder {
    color: var(--re-text-color);
    opacity: 0.35;
}

/*
 * The focus state, in CSS.
 *
 * This was two inline handlers assigning style.borderColor - which means no
 * keyboard focus ring, nothing for :focus-visible, and a border that stayed
 * lit if anything else moved focus away without firing blur.
 */
.auth-input:focus {
    outline: none;
    border-color: var(--re-brand-300, rgba(186, 158, 187, 0.75));
    background: rgba(0, 0, 0, 0.42);
}

.auth-input:focus-visible {
    /* Visible against both the card and the page behind it. */
    outline: 2px solid var(--re-brand-200, rgba(210, 190, 210, 0.9));
    outline-offset: 2px;
}

/* The rule under a field - "At least 6 characters". Body face: it is a
   sentence, not a label, so the pixel grid has no business with it. */
.auth-hint {
    font-family: var(--re-font-body);
    font-size: 0.76rem;
    line-height: 1.4;
    color: var(--re-text-color);
    opacity: 0.5;
}

.auth-check {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0;
    color: var(--re-text-color);
    font-family: var(--re-font-body);
    font-size: 0.86rem;
    opacity: 0.8;
    cursor: pointer;
}

.auth-check input {
    width: 17px;
    height: 17px;
    flex: 0 0 auto;
    cursor: pointer;
    accent-color: var(--re-brand-400, #826b7d);
}

/* The button is .pf-btn; this only makes it fill the card and stand at the
   height a primary action wants. */
.auth-submit {
    justify-content: center;
    width: 100%;
    padding: 14px 18px;
}

/*
 * And this is what keeps it looking like .pf-btn at all.
 *
 * The site's button standard carries
 *
 *   button[type="submit"]:not(.btn-secondary-standard)
 *                        :not(.btn-danger-standard)
 *                        :not(.adm-btn)
 *
 * with !important on background, border and border-radius. When both sides
 * are important the more specific one wins, and every :not(.class) counts as
 * a class - so that selector is (0,4,1) against .pf-btn at (0,1,0), and the
 * login button came out as a pale full-width slab with the wrong radius.
 *
 * The repeated .pf-btn below is what takes this to (0,5,1). It looks like a
 * typo and is not: drop one and the button standard takes the card back.
 * Scoped to .auth-card so register and reset get it for free.
 */
.auth-wrap .auth-card button.pf-btn.pf-btn[type="submit"] {
    border-radius: var(--re-radius-sm, 6px) !important;
    background: rgba(var(--re-brand-400-rgb, 130 107 125), 0.42) !important;
    background-image: none !important;
    border: 1px solid var(--re-brand-400, #826b7d) !important;
    color: var(--re-text-color) !important;
    box-shadow: none !important;
}

.auth-wrap .auth-card button.pf-btn.pf-btn[type="submit"]:hover {
    background: rgba(var(--re-brand-400-rgb, 130 107 125), 0.58) !important;
    border-color: var(--re-brand-200, #d2bed2) !important;
}

/*
 * A wider card, for a form with a message box in it.
 *
 * A login card is narrow because two short fields look silly stretched out.
 * A textarea is the opposite: at 460px a paragraph turns into a column.
 */
.auth-wrap-wide {
    max-width: 660px;
}

/* Filled from the account, and not editable here. Dimmed and with the
   text cursor withheld, so it reads as fixed rather than as broken. */
.auth-input[readonly] {
    opacity: 0.62;
    cursor: not-allowed;
    background: rgba(0, 0, 0, 0.18);
}

.auth-input[readonly]:focus {
    /* No focus tint on a field that cannot be typed into. */
    border-color: var(--page-rule, rgba(130, 107, 125, 0.22));
    background: rgba(0, 0, 0, 0.18);
}

textarea.auth-input {
    min-height: 160px;
    resize: vertical;
    line-height: 1.55;
}

/* The qualifier after a label - "(from your account)". Sits on the label
   row, so it inherits the pixel face and only steps back in opacity. */
.auth-label-note {
    opacity: 0.6;
}

/* A required marker that does not rely on colour alone to say so. */
.auth-required {
    color: #ff9aa2;
}

/* Two buttons side by side - Login and Register on the gate below. */
.auth-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* The gate shown to a signed-out visitor: an icon, a sentence, a way in. */
.auth-gate {
    text-align: center;
}

.auth-gate i {
    font-size: 34px;
    opacity: 0.45;
}

.auth-gate .auth-actions {
    justify-content: center;
}

/*
 * The gate's heading.
 *
 * !important on the size only: theme-color-fix.css sets bare h2 with one, and
 * a class loses to an !important declaration however specific it is unless it
 * carries one too. Same note as .dt-h2 and .home-hero-claim.
 */
.auth-gate-title {
    font-family: var(--re-font-display) !important;
    font-size: 1.35rem !important;
    line-height: 1.15;
    margin: 4px 0 0;
    color: var(--re-text-color);
}

.auth-msg {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    margin: 0;
    padding: 11px 13px;
    border-radius: var(--re-radius-sm, 6px);
    border: 1px solid transparent;
    font-family: var(--re-font-body);
    font-size: 0.86rem;
    line-height: 1.45;
}

.auth-msg-error {
    border-color: rgba(220, 53, 69, 0.5);
    background: rgba(220, 53, 69, 0.14);
    color: #ff9aa2;
}

.auth-msg-ok {
    border-color: rgba(64, 160, 100, 0.5);
    background: rgba(64, 160, 100, 0.14);
    color: #8fe0ac;
}

/* Neither good news nor bad - just the state of things. */
.auth-msg-info {
    border-color: var(--page-rule, rgba(130, 107, 125, 0.3));
    background: rgba(var(--re-brand-400-rgb, 130 107 125), 0.12);
    color: var(--re-text-color);
}

.auth-alt {
    margin: 0;
    text-align: center;
    font-family: var(--re-font-body);
    font-size: 0.86rem;
    color: var(--re-text-color);
    opacity: 0.7;
}

.auth-alt a {
    color: var(--re-brand-200, #d2bed2);
    text-decoration: none;
    font-weight: 600;
}

.auth-alt a:hover {
    text-decoration: underline;
    color: var(--re-brand-200, #d2bed2);
}


/* ===== assets/css/grid-flip.css ===== */
/* ==========================================================================
   Filter animation for the card grids
   ==========================================================================

   The transitions grid-flip.js drives. Only three classes, all added and
   removed by that file - nothing here applies until a filter is used, and
   every one of them is cleaned off the card when the animation ends.

   The transform belongs to the animation for the length of the animation.
   Anything else that transforms a card - a hover lift, say - fights it, so
   the hover treatment on these grids is left to the card's inner elements.
   ========================================================================== */

/* On the compositor for the duration, off it the rest of the time: a
   permanent will-change on every card in a long list is its own cost. */
.gf-item {
    will-change: transform, opacity;
}

/* A card that survived the filter, sliding from where it used to be. */
.gf-move {
    transition: transform 420ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* A card the filter has just brought in: starts under and slightly small. */
.gf-enter {
    opacity: 0;
    transform: translateY(10px) scale(0.96);
}

/* Wins over .gf-enter by being later, which is what releases it. */
.gf-enter-active {
    opacity: 1;
    transform: none;
    transition: opacity 320ms ease,
                transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/*
 * Asked for less motion, gets none. grid-flip.js checks the same query and
 * returns before touching anything, so this is belt and braces - it also
 * covers a card left mid-animation if the setting changes while the page is
 * open.
 */
@media (prefers-reduced-motion: reduce) {
    .gf-item,
    .gf-move,
    .gf-enter,
    .gf-enter-active {
        transition: none !important;
        transform: none !important;
        opacity: 1 !important;
        will-change: auto;
    }
}


/* ===== assets/css/lightbox.css ===== */
/**
 * Lightbox.
 * =========
 *
 * The viewer that replaces `window.open(this.src)`. Everything here is drawn
 * from the design tokens rather than fixed colours, so it follows the active
 * palette the way the rest of the site does - open a screenshot while a Neo
 * Geo track is playing and the chrome around it is the same red as the page
 * behind it.
 *
 * See assets/js/lightbox.js for what opens it.
 */

.re-lightbox {
    position: fixed;
    inset: 0;
    z-index: 21000;              /* over the player (20000) and the nav */
    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(4px, 1.5vw, 18px);
    padding: clamp(12px, 4vw, 48px);

    /* Nearly opaque rather than a light scrim: a screenshot has to be judged
       against something neutral, not against the page bleeding through it. */
    background: rgba(8, 5, 10, 0.88);
    backdrop-filter: blur(10px) saturate(120%);
    -webkit-backdrop-filter: blur(10px) saturate(120%);
    animation: re-lb-in 0.18s ease;
}

@keyframes re-lb-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .re-lightbox { animation: none; }
}

/* The page must not scroll behind the viewer. */
html.re-lightbox-open,
html.re-lightbox-open body {
    overflow: hidden;
}

.re-lb-stage {
    margin: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    min-width: 0;
    max-width: 100%;
    max-height: 100%;
}

.re-lb-media {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    max-width: 100%;
}

.re-lb-img,
.re-lb-video {
    display: block;
    max-width: 100%;
    /* Room for the caption bar under it without the media ever being cropped. */
    max-height: calc(100vh - clamp(96px, 18vh, 180px));
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: var(--re-radius-md);
    border: 1px solid var(--re-border);
    box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
    background: var(--re-surface-sunken);
}

/*
 * A YouTube embed has no intrinsic size, so unlike an image or a video file
 * it cannot size itself from its content - it has to be given a box. 16:9 at
 * the largest that still leaves room for the caption, capped so it does not
 * become a wall on an ultrawide monitor.
 */
.re-lb-frame {
    display: block;
    width: min(90vw, 1280px, calc((100vh - clamp(96px, 18vh, 180px)) * 16 / 9));
    aspect-ratio: 16 / 9;
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-md);
    box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
    background: #000;
}

.re-lb-bar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    flex-wrap: wrap;
    max-width: 90vw;
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--re-text-color);
}

.re-lb-caption { opacity: 0.85; }
.re-lb-count   { opacity: 0.5; white-space: nowrap; }

/* An empty bar should take no room at all. */
.re-lb-bar:not(:has(.re-lb-caption:not(:empty))):not(:has(.re-lb-count:not(:empty))) {
    display: none;
}

/* --- controls ------------------------------------------------------- */

.re-lb-close,
.re-lb-nav {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 44px: the same target floor the header controls use. */
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-sm);
    background: var(--re-surface);
    color: var(--re-text-color);
    cursor: pointer;
    line-height: 1;
    font-size: 20px;
    transition: background var(--re-transition), border-color var(--re-transition),
                transform var(--re-transition);
}

.re-lb-close:hover,
.re-lb-nav:hover,
.re-lb-close:focus-visible,
.re-lb-nav:focus-visible {
    background: var(--re-surface-hover);
    border-color: var(--re-border-strong);
    color: var(--re-brand-100);
}

.re-lb-nav:active { transform: scale(0.94); }

.re-lb-close {
    position: absolute;
    top: clamp(10px, 2.5vw, 22px);
    right: clamp(10px, 2.5vw, 22px);
    font-size: 15px;
}

.re-lb-nav { font-size: 26px; }

/*
 * On a phone the arrows sit over the bottom corners of the image rather than
 * beside it: at 375px there is no room for a column of controls on each side
 * without squeezing the picture to nothing.
 */
@media (max-width: 640px) {
    .re-lightbox { padding: 12px; }

    .re-lb-nav {
        position: absolute;
        bottom: 16px;
        z-index: 1;
    }

    .re-lb-prev { left: 16px; }
    .re-lb-next { right: 16px; }

    .re-lb-img { max-height: calc(100vh - 190px); }
}

/* --- what opens it -------------------------------------------------- */

/*
 * The affordance. An image that enlarges has to look like one before it is
 * clicked, or nobody clicks it.
 */
[data-lightbox],
.article-content img {
    cursor: zoom-in;
}

[data-lightbox]:hover {
    filter: brightness(1.06);
}


/* ------------------------------------------------------------------ *
 * Gallery tiles
 * ------------------------------------------------------------------ */

/*
 * A video tile and a photo tile look identical until you say otherwise, so
 * anything playable gets a badge. Without it the only way to find the video
 * in a gallery is to click everything.
 */
.re-media-tile {
    position: relative;
    display: block;
    line-height: 0;      /* no descender gap under the thumbnail */
}

.re-media-tile > img,
.re-media-tile > video {
    display: block;
}

.re-media-play {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 54px;
    height: 54px;
    border-radius: 50%;
    border: 1px solid var(--re-border-strong);
    background: rgba(8, 5, 10, 0.62);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    pointer-events: none;   /* the click belongs to the thumbnail beneath */
    transition: background var(--re-transition), transform var(--re-transition);
}

/* The triangle, drawn with a border rather than shipped as an asset. */
.re-media-play::after {
    content: "";
    position: absolute;
    left: 52%;
    top: 50%;
    transform: translate(-50%, -50%);
    border-style: solid;
    border-width: 9px 0 9px 15px;
    border-color: transparent transparent transparent var(--re-brand-100);
}

.re-media-tile:hover .re-media-play {
    background: rgba(8, 5, 10, 0.78);
    transform: translate(-50%, -50%) scale(1.06);
}

.re-media-tile.is-playable { cursor: pointer; }


/* ===== assets/css/search.css ===== */
/**
 * Site search.
 * ============
 *
 * The trigger in the navbar, the overlay it opens, and the results page.
 * Everything is drawn from the design tokens, so it follows the palette with
 * the rest of the site.
 */

/* ------------------------------------------------------------------ *
 * The trigger
 * ------------------------------------------------------------------ */

.re-search-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 34px;
    padding: 0 10px;
    margin-left: auto;
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-sm);
    /* Matches .header-controls-lang-toggle, the control it sits nearest. */
    background: var(--re-surface-raised);
    color: var(--re-text-color);
    cursor: pointer;
    font-size: 13px;
    line-height: 1;
    transition: background var(--re-transition), border-color var(--re-transition);
}

.re-search-trigger:hover,
.re-search-trigger:focus-visible {
    background: var(--re-surface-hover);
    border-color: var(--re-border-strong);
}

/* The keyboard hint. Silkscreen, so the pixel grid holds it at 8px. */
.re-search-hint {
    font-family: var(--re-font-ui);
    opacity: 0.55;
    border: 1px solid var(--re-border);
    border-radius: 3px;
    padding: 2px 5px;
    line-height: 1;
}

/* No room for a hint beside the hamburger. */
@media (max-width: 767px) {
    .re-search-hint { display: none; }
    .re-search-trigger { margin-left: 0; margin-right: 8px; }
}

/* ------------------------------------------------------------------ *
 * The overlay
 * ------------------------------------------------------------------ */

.re-search {
    position: fixed;
    inset: 0;
    z-index: 21500;             /* over the lightbox's 21000 and the player */
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding: clamp(16px, 8vh, 96px) 16px 16px;
    /*
     * Dark enough to push the page back, light enough that the sheet still
     * has somewhere to lift off from. At 0.72 the scrim was almost as dark as
     * the panel on top of it and the whole overlay read as one flat shape.
     */
    background: color-mix(in srgb, var(--re-brand-900) 62%, transparent);
    backdrop-filter: blur(10px) saturate(120%);
    -webkit-backdrop-filter: blur(10px) saturate(120%);
    animation: re-search-in 0.14s ease;
}

@keyframes re-search-in { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
    .re-search { animation: none; }
}

html.re-search-open,
html.re-search-open body { overflow: hidden; }

.re-search-sheet {
    width: min(680px, 100%);
    max-height: 100%;
    display: flex;
    flex-direction: column;
    /*
     * --re-surface is the site's default panel, and it is the wrong token
     * here. It is 55% ink, tuned to sit on the PAGE - a mid-dark ground with
     * a particle field behind it. Over a scrim it has nothing to be lighter
     * than, so the sheet vanished into its own backdrop.
     *
     * brand-800 is one step up the ladder from the darkest, at most of the
     * way to opaque: still glass, still the palette, but unmistakably a panel
     * sitting in front of something.
     */
    background: var(--re-surface);
    background: color-mix(in srgb, var(--re-brand-800) 88%, transparent);
    backdrop-filter: var(--re-glass);
    -webkit-backdrop-filter: var(--re-glass);
    border: 1px solid var(--re-border-strong);
    border-radius: var(--re-radius-lg);
    box-shadow:
        0 28px 80px rgba(0, 0, 0, 0.6),
        inset 0 1px 0 0 rgba(255, 255, 255, 0.06);
    overflow: hidden;
}

/* --- the input row -------------------------------------------------- */

.re-search-box {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--re-border-faint);
    transition: border-color var(--re-transition), background var(--re-transition);
}

/*
 * The focus ring goes on the ROW, not the input.
 *
 * theme-tokens.css draws a 2px --re-brand-200 outline on anything focusable,
 * which is right for a button but wrong here: it traced a hard rectangle
 * around a borderless field in the middle of a panel and read as a browser
 * default rather than part of the design. The row lighting up instead says
 * the same thing in the site's own language.
 */
.re-search-box:focus-within {
    border-bottom-color: var(--re-brand-300);
    background: color-mix(in srgb, var(--re-brand-400) 8%, transparent);
}

.re-search-icon { opacity: 0.5; font-size: 15px; }

.re-search-input {
    flex: 1 1 auto;
    min-width: 0;
    background: none;
    border: 0;
    outline: none;
    color: var(--re-text-color);
    /* Body face, not Silkscreen: this is a field someone types a sentence
       into, and 8px pixel type is the wrong tool for that. */
    font-family: var(--re-font-body);
    font-size: 1rem;
}

/* Suppressed here because the row above shows the focus instead. */
.re-search-input:focus,
.re-search-input:focus-visible { outline: none !important; box-shadow: none !important; }

.re-search-input::placeholder { color: var(--re-text-muted); }

/* The browser's own clear button sits badly on a dark glass field. */
.re-search-input::-webkit-search-cancel-button { display: none; }

.re-search-esc {
    flex: 0 0 auto;
    font-family: var(--re-font-ui);
    background: none;
    border: 1px solid var(--re-border);
    border-radius: 3px;
    padding: 4px 6px;
    color: var(--re-text-color);
    opacity: 0.5;
    cursor: pointer;
}

.re-search-esc:hover { opacity: 1; }

/* --- results -------------------------------------------------------- */

.re-search-results {
    overflow-y: auto;
    overscroll-behavior: contain;   /* do not scroll the page behind it */
    padding: 6px;
}

.re-search-group { padding: 6px 0; }

.re-search-group-label {
    display: block;
    padding: 4px 10px;
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--re-brand-200);
    opacity: 0.7;
}

.re-search-hit,
.re-search-all {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 9px 10px;
    border-radius: var(--re-radius-sm);
    color: var(--re-text-color);
    text-decoration: none;
}

.re-search-hit:hover,
.re-search-hit.is-active,
.re-search-all:hover,
.re-search-all.is-active {
    background: var(--re-surface-hover);
}

.re-search-hit-icon { flex: 0 0 auto; opacity: 0.5; font-size: 14px; }

.re-search-hit-body { min-width: 0; }

.re-search-hit-title,
.re-search-hit-snip {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.re-search-hit-title { font-family: var(--re-font-body); font-size: 0.92rem; }

.re-search-hit-snip {
    font-family: var(--re-font-body);
    font-size: 0.78rem;
    opacity: 0.55;
    margin-top: 2px;
}

/* The matched run. A background would fight the glass, so it is the accent
   colour and a little weight instead. */
.re-search-results mark {
    background: none;
    color: var(--re-brand-100);
    font-weight: 700;
}

.re-search-all {
    justify-content: center;
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    border-top: 1px solid var(--re-border-faint);
    border-radius: 0;
    margin-top: 4px;
    opacity: 0.75;
}

.re-search-empty {
    padding: 26px 16px;
    margin: 0;
    text-align: center;
    font-family: var(--re-font-body);
    color: var(--re-text-muted);
}

/* --- the key legend ------------------------------------------------- */

/*
 * The legend was 40% opacity with hairline chips on a dark panel, which came
 * out as three grey smudges - you could see that something was written there
 * and not what. 8px pixel type has no weight to spare, so it needs the
 * contrast instead: the row at 0.65 and the keys drawn as filled chips.
 */
.re-search-foot {
    display: flex;
    gap: 18px;
    justify-content: center;
    align-items: center;
    padding: 10px 8px;
    border-top: 1px solid var(--re-border-faint);
    background: color-mix(in srgb, var(--re-brand-900) 45%, transparent);
    font-family: var(--re-font-ui);
    color: var(--re-text-color);
    opacity: 0.65;
}

.re-search-foot kbd {
    font-family: var(--re-font-ui);
    background: var(--re-surface-raised);
    border: 1px solid var(--re-border-strong);
    border-radius: 3px;
    padding: 3px 5px;
    margin-right: 3px;
    color: var(--re-brand-100);
    line-height: 1;
}

/* A phone has no arrow keys and the legend is just noise. */
@media (max-width: 640px) {
    .re-search-foot { display: none; }
    .re-search { padding-top: 12px; }
}

/* ------------------------------------------------------------------ *
 * The results page
 * ------------------------------------------------------------------ */

.sr-form {
    display: flex;
    gap: 10px;
    align-items: center;
    margin: 0 0 26px;
}

.sr-input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 11px 14px;
    background: var(--re-surface-sunken);
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-sm);
    color: var(--re-text-color);
    font-family: var(--re-font-body);
    font-size: 1rem;
}

.sr-input:focus {
    outline: none;
    border-color: var(--re-border-strong);
}

.sr-go { flex: 0 0 auto; }

.sr-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 2px;
}

.sr-link {
    display: grid;
    grid-template-columns: 56px minmax(0, 1fr) auto;
    align-items: center;
    gap: 16px;
    padding: 12px 10px;
    border-bottom: 1px solid var(--page-rule, rgba(130, 107, 125, 0.22));
    text-decoration: none;
    border-radius: var(--re-radius-sm);
    transition: background 0.16s ease, padding-left 0.16s ease;
}

.sr-link:hover {
    background: var(--re-surface-hover);
    padding-left: 18px;
}

.sr-thumb {
    width: 56px;
    height: 42px;
    border-radius: var(--re-radius-sm);
    overflow: hidden;
    background: var(--re-surface-sunken);
    display: flex;
    align-items: center;
    justify-content: center;
}

.sr-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.sr-thumb-empty { color: var(--re-brand-300); opacity: 0.6; font-size: 16px; }

.sr-title {
    display: block;
    font-family: var(--re-font-body);
    font-weight: 700;
    font-size: 1rem;
    color: var(--re-text-color);
}

.sr-snippet {
    display: block;
    font-family: var(--re-font-body);
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--re-text-color);
    opacity: 0.6;
    margin-top: 3px;
}

.sr-kind {
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--re-brand-300);
    opacity: 0.7;
    white-space: nowrap;
}

@media (max-width: 640px) {
    .sr-link { grid-template-columns: minmax(0, 1fr); gap: 6px; }
    .sr-thumb, .sr-kind { display: none; }
}

/* ------------------------------------------------------------------ *
 * A track arrived at from a search result
 * ------------------------------------------------------------------ */

/*
 * Music has no page of its own, so a search result sends you to the list with
 * the track named. Without a marker you land in 368 rows with no idea which
 * one you asked for.
 */
.mu-track.is-found {
    animation: re-found 2.4s ease;
}

@keyframes re-found {
    0%, 70% { background: var(--re-surface-active); }
    100%    { background: transparent; }
}

@media (prefers-reduced-motion: reduce) {
    .mu-track.is-found { animation: none; background: var(--re-surface-active); }
}


/* ===== assets/css/silkscreen-pixel-grid.css ===== */
/**
 * Silkscreen on the pixel grid.
 * =============================
 *
 * Silkscreen is a bitmap face drawn on an 8px em. At any other size the
 * browser resamples glyphs that were never meant to be resampled, and the
 * stems that make it legible land between pixels. Audited live, the site was
 * drawing it at NINE different sizes - 8, 8.96, 9, 9.28, 9.6, 9.92, 10.56,
 * 10.88 and 16px - because 45 stylesheets each set their own, most in rem.
 *
 * This file is the single place the size is decided. The selector list is
 * GENERATED, not written: every rule in the 23 linked stylesheets that asks
 * for the UI face contributes its selectors here, so the list cannot drift
 * from the rules it mirrors. Regenerate it rather than editing it by hand.
 *
 * Three things make the text pixel-exact:
 *
 *   font-size          8px, from --re-size-ui, the one token that defines it.
 *   font-smoothing     none. Antialiasing is what greys the edge of a stem
 *                      that is meant to be one pixel wide, and every one of
 *                      these runs was computing to `antialiased`.
 *   letter-spacing     whole pixels. It was declared in em nearly everywhere,
 *                      which at 8px gives fractions - 0.96px, 1.12px, 1.28px,
 *                      1.62px - and a fractional advance puts the next glyph
 *                      off the grid. Each value is rounded to the nearest
 *                      pixel, so the tracking a rule asked for is kept.
 *
 * It loads last, after page-refined.css, because it has to outrank every file
 * it corrects. Only stylesheets actually linked from templates/header.php were
 * scanned - the dead footer-layout.css and footer-copyright-fix.css still
 * carry Silkscreen rules, and pulling their selectors in here would have
 * brought long-disabled styling back to life.
 */

/* ------------------------------------------------------------------ *
 * One size, no antialiasing
 * ------------------------------------------------------------------ */
#autoResolveBtn,
#calibrationToggleBtn,
.ab-profile-tagline,
.adm-btn,
.adm-label,
.adm-nav-item,
.adm-panel-note,
.adm-sub,
.adm-tab,
.adm-table th,
.adm-tag,
.admin-badge,
.admin-breadcrumb,
.admin-dashboard .pagination a,
.admin-header .btn,
.admin-page-header .btn,
.analytics-badge,
.article-category,
.article-container-dark .btn,
.article-container-light .btn,
.article-date,
.article-edit-button,
.article-featured-badge,
.article-meta,
.article-meta-item,
.article-read-more,
.article-read-more *,
.article-tag,
.article-views,
.bot-filter-btn,
.bot-filter-btn.bots-excluded,
.bot-filter-btn.bots-included,
.breadcrumb,
.breadcrumb *,
.breadcrumb-item,
.breadcrumb-item *,
.btn,
.btn *,
.btn-primary,
.btn-primary-standard,
.btn-secondary-standard,
.btn[href*="section=diagnostics"],
.btn[href*="section=settings"],
.bulk-drop-note,
.bulk-status,
.card-badge span,
.card-btn-primary,
.card-tag,
.copyright-text,
.cv-exp-tags li,
.cv-exp-when,
.cv-exp-where,
.cv-skill-cat > h3,
.cv-skill-pct,
.dropdown-item,
.dropdown.header-controls-lang .dropdown-item,
.dt-fact-label,
.dt-figure-hint,
.dt-score-badge,
.dt-shot-cap,
.dt-tag,
.experience-date,
.experience-tag,
.filter-btn,
.filter-btn-count,
.filter-section-title,
.footer-copy,
.footer-cta,
.footer-eyebrow,
.footer-legal a,
.footer-links a,
.footer-role,
.footer-status,
.footer-top,
.footer:not(.site-footer):not(.server-info-footer) *,
.game-card .progress .row,
.game-card .rating-badge,
.game-card .status-badge,
.game-card .tags span,
.game-card.favorite .label,
.header-controls-lang .dropdown-item,
.header-controls-panel,
.header-controls-register,
.auth-label,
.home-btn,
.home-eyebrow,
.home-media-empty,
.home-meta,
.home-platform-marks .platform-badge-text,
.home-platforms-key,
.home-post-cat,
.home-post-date,
.home-section-link,
.home-stat-key,
.home-tag,
.intro-subtitle,
.lb-tag,
.lb-updated,
.map-calibration-btn,
.map-legend,
.map-legend span,
.map-stats,
.map-stats .stat-row,
.map-stats .stat-value,
.map-stats .stats-header,
.mq-btn,
.mq-count,
.mq-pos,
.mu-duration,
.mu-index,
.mu-system,
.mu-track.is-draft .mu-title::after,
.nav-link,
.nav-tabs .nav-link,
.navbar .dropdown-menu .dropdown-item,
.navbar .dropdown-toggle,
.navbar .nav-link,
.navbar-nav .nav-link,
.navbar-nav a,
.page-chip,
.page-head-eyebrow,
.page-head-meta,
.page-legal-updated,
.page-section-note,
.page-subtitle,
.pagination a,
.pagination span,
.pagination-btn,
.pagination-controls a,
.pagination-controls span,
.pagination-page,
.palette-follow,
.palette-group,
.palette-name,
.palette-note,
.pf-btn,
.pf-media-empty,
.pf-release,
.pf-tags li,
.pg-chip,
.pg-filter-label,
.platform-badge-text,
.playlist-empty,
.playlist-item-artist,
.playlist-item-title,
.playlist-note,
.playlist-reset,
.playlist-toggle-btn,
.profile-tagline,
.quick-link-subtitle,
.re-lb-bar,
.re-search-all,
.re-search-esc,
.re-search-foot,
.re-search-foot kbd,
.re-search-group-label,
.re-search-hint,
.resolve-ip-btn,
.rs-aside-label,
.rs-contact,
.rs-rec-role,
.rs-when,
.rs-where,
.saved-theme-btn,
.section-header-text .section-meta,
.section-header-text .section-subtitle,
.server-info-footer table,
.server-info-footer table *,
.server-info-footer tbody,
.server-info-footer tbody *,
.server-info-footer td,
.server-info-footer th,
.server-info-footer thead,
.server-info-footer thead *,
.server-stat-item,
.silkscreen-jp,
.site-message-container .message-meta,
.site-message-dismiss,
.site-message-priority,
.skill-percent,
.sr-kind,
.theme-angle-presets button,
.theme-btn,
.theme-preset-btn span,
.theme-saved-date,
.theme-section h4,
.theme-toast button,
.time-current,
.time-total,
.track-artist,
.track-title,
.user-role-display,
[data-about-section] > .section-subtitle,
[href*="visitors_page"],
[style*="Silkscreen"],
[style*="font-family: Silkscreen"],
[style*="font-family:Silkscreen"],
[style*="padding: 10px 20px"],
a.btn,
a.btn *,
a[href*="page=admin"][href*="visitors_page"],
a[href*="visitors_page"][style*="Albert Sans"],
body > .dropdown-menu.header-controls-lang-portal .dropdown-item,
body[data-page*="admin"] .pagination a,
body[data-page*="admin"] .pagination span,
body[data-page*="admin"] [style*="pagination"] a,
body[data-page*="admin"] [style*="pagination"] span,
body[data-page*="admin"] a[style*="Albert Sans"],
body[data-page*="admin"] span[style*="Albert Sans"],
body[data-page="admin"] #autoResolveBtn,
body[data-page="admin"] #calibrationToggleBtn,
body[data-page="admin"] .admin-page .admin-breadcrumb,
body[data-page="admin"] .admin-page .admin-meta,
body[data-page="admin"] .admin-page .admin-stat-label,
body[data-page="admin"] .admin-page .badge,
body[data-page="admin"] .admin-page .badge[style*="Silkscreen"],
body[data-page="admin"] .admin-page .bot-filter-btn,
body[data-page="admin"] .admin-page .breadcrumb,
body[data-page="admin"] .admin-page .breadcrumb *,
body[data-page="admin"] .admin-page .btn,
body[data-page="admin"] .admin-page .form-label,
body[data-page="admin"] .admin-page .resolve-ip-btn,
body[data-page="admin"] .admin-page button,
body[data-page="admin"] .admin-page label,
body[data-page="admin"] .admin-page small,
body[data-page="admin"] .admin-page th,
body[data-page="admin"] [href*="page=admin"][href*="visitors_page"],
body[data-page="admin"] [style*="font-family: 'Silkscreen'"],
body[data-page="admin"] a[href*="visitors_page"],
body[data-page="admin"] a[style*="font-family: 'Albert Sans'"],
body[data-page="admin"] span[style*="font-family: 'Albert Sans'"],
body[data-page="admin"] span[style*="font-family: 'Silkscreen'"],
body[data-page="contact"] .btn-primary,
body[data-page="contact"] button[type="submit"],
button,
button.pg-chip,
button[class*="resolve-ip"],
button[onclick*="autoResolveAll"],
button[onclick*="toggleBotFilter"],
button[onclick*="toggleCalibrationMode"],
button[type="submit"]:not(.btn-secondary-standard):not(.btn-danger-standard),
div[style*="pagination"] a,
div[style*="pagination"] span,
footer:not(.server-info-footer) .footer [style*="Silkscreen"],
footer:not(.server-info-footer) [style*="Silkscreen"],
footer:not(.site-footer):not(.server-info-footer) *,
h1.filter-section-title,
h2.filter-section-title,
h3.filter-section-title,
h4.filter-section-title,
h5.filter-section-title,
h6.filter-section-title,
input[type="button"],
input[type="submit"],
nav .nav-link,
select.pg-select,
span.dropdown.header-controls-lang .dropdown-item,
span[style*="Albert Sans"][style*="min-width: 40px"],
span[style*="font-family: Silkscreen"],
span[style*="font-family:Silkscreen"],
table .badge,
table .resolve-ip-btn,
table tbody td,
table thead th {
    font-size: var(--re-size-ui, 8px) !important;
}

/* ------------------------------------------------------------------ *
 * No antialiasing
 * ------------------------------------------------------------------ *
 *
 * Descendants are included here and NOT in the size rule above, and the
 * split is the whole point.
 *
 * Smoothing has to reach them: japanese-support.css sets
 * `-webkit-font-smoothing: antialiased` on bare `a` and `h1`-`h6`, and a rule
 * that matches an element beats the value it would otherwise inherit from its
 * parent - so the theme toggle's label and the profile link stayed antialiased
 * inside a panel that was already set to none.
 *
 * Size must NOT: a descendant with no size of its own already inherits the
 * 8px above, and the ones that DO set their own mean it. The theme toggle's
 * switch is a 26px SVG sitting inside the panel; `font-size: 8px !important`
 * on descendants would have shrunk it to a third of its size.
 *
 * A pseudo-element gets no ` *` variant. `::after *` is not a valid selector -
 * a pseudo-element cannot have descendants - and ONE invalid selector makes a
 * browser drop the ENTIRE list, because a CSS selector list is all-or-nothing.
 * That is not theoretical: `.mu-track.is-draft .mu-title::after` arrived with
 * the draft badge on the music page, and from that moment this rule was being
 * discarded silently and every Silkscreen run on the site was antialiased
 * again. Nothing warned; the CSS simply stopped existing.
 *
 * For the same reason, do not try to win a specificity fight by prefixing
 * these selectors with `html` - that is 452 more chances to write one the
 * parser rejects. Fix the rule that outranks this instead.
 */
#autoResolveBtn,
#autoResolveBtn *,
#calibrationToggleBtn,
#calibrationToggleBtn *,
.ab-profile-tagline,
.ab-profile-tagline *,
.adm-btn,
.adm-btn *,
.adm-label,
.adm-label *,
.adm-nav-item,
.adm-nav-item *,
.adm-panel-note,
.adm-panel-note *,
.adm-sub,
.adm-sub *,
.adm-tab,
.adm-tab *,
.adm-table th,
.adm-table th *,
.adm-tag,
.adm-tag *,
.admin-badge,
.admin-badge *,
.admin-breadcrumb,
.admin-breadcrumb *,
.admin-dashboard .pagination a,
.admin-dashboard .pagination a *,
.admin-header .btn,
.admin-header .btn *,
.admin-page-header .btn,
.admin-page-header .btn *,
.analytics-badge,
.analytics-badge *,
.article-category,
.article-category *,
.article-container-dark .btn,
.article-container-dark .btn *,
.article-container-light .btn,
.article-container-light .btn *,
.article-date,
.article-date *,
.article-edit-button,
.article-edit-button *,
.article-featured-badge,
.article-featured-badge *,
.article-meta,
.article-meta *,
.article-meta-item,
.article-meta-item *,
.article-read-more,
.article-read-more *,
.article-read-more *,
.article-read-more * *,
.article-tag,
.article-tag *,
.article-views,
.article-views *,
.bot-filter-btn,
.bot-filter-btn *,
.bot-filter-btn.bots-excluded,
.bot-filter-btn.bots-excluded *,
.bot-filter-btn.bots-included,
.bot-filter-btn.bots-included *,
.breadcrumb,
.breadcrumb *,
.breadcrumb *,
.breadcrumb * *,
.breadcrumb-item,
.breadcrumb-item *,
.breadcrumb-item *,
.breadcrumb-item * *,
.btn,
.btn *,
.btn *,
.btn * *,
.btn-primary,
.btn-primary *,
.btn-primary-standard,
.btn-primary-standard *,
.btn-secondary-standard,
.btn-secondary-standard *,
.btn[href*="section=diagnostics"],
.btn[href*="section=diagnostics"] *,
.btn[href*="section=settings"],
.btn[href*="section=settings"] *,
.bulk-drop-note,
.bulk-drop-note *,
.bulk-status,
.bulk-status *,
.card-badge span,
.card-badge span *,
.card-btn-primary,
.card-btn-primary *,
.card-tag,
.card-tag *,
.copyright-text,
.copyright-text *,
.cv-exp-tags li,
.cv-exp-tags li *,
.cv-exp-when,
.cv-exp-when *,
.cv-exp-where,
.cv-exp-where *,
.cv-skill-cat > h3,
.cv-skill-cat > h3 *,
.cv-skill-pct,
.cv-skill-pct *,
.dropdown-item,
.dropdown-item *,
.dropdown.header-controls-lang .dropdown-item,
.dropdown.header-controls-lang .dropdown-item *,
.dt-fact-label,
.dt-fact-label *,
.dt-figure-hint,
.dt-figure-hint *,
.dt-score-badge,
.dt-score-badge *,
.dt-shot-cap,
.dt-shot-cap *,
.dt-tag,
.dt-tag *,
.experience-date,
.experience-date *,
.experience-tag,
.experience-tag *,
.filter-btn,
.filter-btn *,
.filter-btn-count,
.filter-btn-count *,
.filter-section-title,
.filter-section-title *,
.footer-copy,
.footer-copy *,
.footer-cta,
.footer-cta *,
.footer-eyebrow,
.footer-eyebrow *,
.footer-legal a,
.footer-legal a *,
.footer-links a,
.footer-links a *,
.footer-role,
.footer-role *,
.footer-status,
.footer-status *,
.footer-top,
.footer-top *,
.footer:not(.site-footer):not(.server-info-footer) *,
.footer:not(.site-footer):not(.server-info-footer) * *,
.game-card .progress .row,
.game-card .progress .row *,
.game-card .rating-badge,
.game-card .rating-badge *,
.game-card .status-badge,
.game-card .status-badge *,
.game-card .tags span,
.game-card .tags span *,
.game-card.favorite .label,
.game-card.favorite .label *,
.header-controls-lang .dropdown-item,
.header-controls-lang .dropdown-item *,
.header-controls-panel,
.header-controls-panel *,
.header-controls-register,
.header-controls-register *,
.auth-label,
.auth-label *,
.home-btn,
.home-btn *,
.home-eyebrow,
.home-eyebrow *,
.home-media-empty,
.home-media-empty *,
.home-meta,
.home-meta *,
.home-platform-marks .platform-badge-text,
.home-platform-marks .platform-badge-text *,
.home-platforms-key,
.home-platforms-key *,
.home-post-cat,
.home-post-cat *,
.home-post-date,
.home-post-date *,
.home-section-link,
.home-section-link *,
.home-stat-key,
.home-stat-key *,
.home-tag,
.home-tag *,
.intro-subtitle,
.intro-subtitle *,
.lb-tag,
.lb-tag *,
.lb-updated,
.lb-updated *,
.map-calibration-btn,
.map-calibration-btn *,
.map-legend,
.map-legend *,
.map-legend span,
.map-legend span *,
.map-stats,
.map-stats *,
.map-stats .stat-row,
.map-stats .stat-row *,
.map-stats .stat-value,
.map-stats .stat-value *,
.map-stats .stats-header,
.map-stats .stats-header *,
.mq-btn,
.mq-btn *,
.mq-count,
.mq-count *,
.mq-pos,
.mq-pos *,
.mu-duration,
.mu-duration *,
.mu-index,
.mu-index *,
.mu-system,
.mu-system *,
.mu-track.is-draft .mu-title::after,
.nav-link,
.nav-link *,
.nav-tabs .nav-link,
.nav-tabs .nav-link *,
.navbar .dropdown-menu .dropdown-item,
.navbar .dropdown-menu .dropdown-item *,
.navbar .dropdown-toggle,
.navbar .dropdown-toggle *,
.navbar .nav-link,
.navbar .nav-link *,
.navbar-nav .nav-link,
.navbar-nav .nav-link *,
.navbar-nav a,
.navbar-nav a *,
.page-chip,
.page-chip *,
.page-head-eyebrow,
.page-head-eyebrow *,
.page-head-meta,
.page-head-meta *,
.page-legal-updated,
.page-legal-updated *,
.page-section-note,
.page-section-note *,
.page-subtitle,
.page-subtitle *,
.pagination a,
.pagination a *,
.pagination span,
.pagination span *,
.pagination-btn,
.pagination-btn *,
.pagination-controls a,
.pagination-controls a *,
.pagination-controls span,
.pagination-controls span *,
.pagination-page,
.pagination-page *,
.palette-follow,
.palette-follow *,
.palette-group,
.palette-group *,
.palette-name,
.palette-name *,
.palette-note,
.palette-note *,
.pf-btn,
.pf-btn *,
.pf-media-empty,
.pf-media-empty *,
.pf-release,
.pf-release *,
.pf-tags li,
.pf-tags li *,
.pg-chip,
.pg-chip *,
.pg-filter-label,
.pg-filter-label *,
.platform-badge-text,
.platform-badge-text *,
.playlist-empty,
.playlist-empty *,
.playlist-item-artist,
.playlist-item-artist *,
.playlist-item-title,
.playlist-item-title *,
.playlist-note,
.playlist-note *,
.playlist-reset,
.playlist-reset *,
.playlist-toggle-btn,
.playlist-toggle-btn *,
.profile-tagline,
.profile-tagline *,
.quick-link-subtitle,
.quick-link-subtitle *,
.re-lb-bar,
.re-lb-bar *,
.re-search-all,
.re-search-all *,
.re-search-esc,
.re-search-esc *,
.re-search-foot,
.re-search-foot *,
.re-search-foot kbd,
.re-search-foot kbd *,
.re-search-group-label,
.re-search-group-label *,
.re-search-hint,
.re-search-hint *,
.resolve-ip-btn,
.resolve-ip-btn *,
.rs-aside-label,
.rs-aside-label *,
.rs-contact,
.rs-contact *,
.rs-rec-role,
.rs-rec-role *,
.rs-when,
.rs-when *,
.rs-where,
.rs-where *,
.saved-theme-btn,
.saved-theme-btn *,
.section-header-text .section-meta,
.section-header-text .section-meta *,
.section-header-text .section-subtitle,
.section-header-text .section-subtitle *,
.server-info-footer table,
.server-info-footer table *,
.server-info-footer table *,
.server-info-footer table * *,
.server-info-footer tbody,
.server-info-footer tbody *,
.server-info-footer tbody *,
.server-info-footer tbody * *,
.server-info-footer td,
.server-info-footer td *,
.server-info-footer th,
.server-info-footer th *,
.server-info-footer thead,
.server-info-footer thead *,
.server-info-footer thead *,
.server-info-footer thead * *,
.server-stat-item,
.server-stat-item *,
.silkscreen-jp,
.silkscreen-jp *,
.site-message-container .message-meta,
.site-message-container .message-meta *,
.site-message-dismiss,
.site-message-dismiss *,
.site-message-priority,
.site-message-priority *,
.skill-percent,
.skill-percent *,
.sr-kind,
.sr-kind *,
.theme-angle-presets button,
.theme-angle-presets button *,
.theme-btn,
.theme-btn *,
.theme-preset-btn span,
.theme-preset-btn span *,
.theme-saved-date,
.theme-saved-date *,
.theme-section h4,
.theme-section h4 *,
.theme-toast button,
.theme-toast button *,
.time-current,
.time-current *,
.time-total,
.time-total *,
.track-artist,
.track-artist *,
.track-title,
.track-title *,
.user-role-display,
.user-role-display *,
[data-about-section] > .section-subtitle,
[data-about-section] > .section-subtitle *,
[href*="visitors_page"],
[href*="visitors_page"] *,
[style*="Silkscreen"],
[style*="Silkscreen"] *,
[style*="font-family: Silkscreen"],
[style*="font-family: Silkscreen"] *,
[style*="font-family:Silkscreen"],
[style*="font-family:Silkscreen"] *,
[style*="padding: 10px 20px"],
[style*="padding: 10px 20px"] *,
a.btn,
a.btn *,
a.btn *,
a.btn * *,
a[href*="page=admin"][href*="visitors_page"],
a[href*="page=admin"][href*="visitors_page"] *,
a[href*="visitors_page"][style*="Albert Sans"],
a[href*="visitors_page"][style*="Albert Sans"] *,
body > .dropdown-menu.header-controls-lang-portal .dropdown-item,
body > .dropdown-menu.header-controls-lang-portal .dropdown-item *,
body[data-page*="admin"] .pagination a,
body[data-page*="admin"] .pagination a *,
body[data-page*="admin"] .pagination span,
body[data-page*="admin"] .pagination span *,
body[data-page*="admin"] [style*="pagination"] a,
body[data-page*="admin"] [style*="pagination"] a *,
body[data-page*="admin"] [style*="pagination"] span,
body[data-page*="admin"] [style*="pagination"] span *,
body[data-page*="admin"] a[style*="Albert Sans"],
body[data-page*="admin"] a[style*="Albert Sans"] *,
body[data-page*="admin"] span[style*="Albert Sans"],
body[data-page*="admin"] span[style*="Albert Sans"] *,
body[data-page="admin"] #autoResolveBtn,
body[data-page="admin"] #autoResolveBtn *,
body[data-page="admin"] #calibrationToggleBtn,
body[data-page="admin"] #calibrationToggleBtn *,
body[data-page="admin"] .admin-page .admin-breadcrumb,
body[data-page="admin"] .admin-page .admin-breadcrumb *,
body[data-page="admin"] .admin-page .admin-meta,
body[data-page="admin"] .admin-page .admin-meta *,
body[data-page="admin"] .admin-page .admin-stat-label,
body[data-page="admin"] .admin-page .admin-stat-label *,
body[data-page="admin"] .admin-page .badge,
body[data-page="admin"] .admin-page .badge *,
body[data-page="admin"] .admin-page .badge[style*="Silkscreen"],
body[data-page="admin"] .admin-page .badge[style*="Silkscreen"] *,
body[data-page="admin"] .admin-page .bot-filter-btn,
body[data-page="admin"] .admin-page .bot-filter-btn *,
body[data-page="admin"] .admin-page .breadcrumb,
body[data-page="admin"] .admin-page .breadcrumb *,
body[data-page="admin"] .admin-page .breadcrumb *,
body[data-page="admin"] .admin-page .breadcrumb * *,
body[data-page="admin"] .admin-page .btn,
body[data-page="admin"] .admin-page .btn *,
body[data-page="admin"] .admin-page .form-label,
body[data-page="admin"] .admin-page .form-label *,
body[data-page="admin"] .admin-page .resolve-ip-btn,
body[data-page="admin"] .admin-page .resolve-ip-btn *,
body[data-page="admin"] .admin-page button,
body[data-page="admin"] .admin-page button *,
body[data-page="admin"] .admin-page label,
body[data-page="admin"] .admin-page label *,
body[data-page="admin"] .admin-page small,
body[data-page="admin"] .admin-page small *,
body[data-page="admin"] .admin-page th,
body[data-page="admin"] .admin-page th *,
body[data-page="admin"] [href*="page=admin"][href*="visitors_page"],
body[data-page="admin"] [href*="page=admin"][href*="visitors_page"] *,
body[data-page="admin"] [style*="font-family: 'Silkscreen'"],
body[data-page="admin"] [style*="font-family: 'Silkscreen'"] *,
body[data-page="admin"] a[href*="visitors_page"],
body[data-page="admin"] a[href*="visitors_page"] *,
body[data-page="admin"] a[style*="font-family: 'Albert Sans'"],
body[data-page="admin"] a[style*="font-family: 'Albert Sans'"] *,
body[data-page="admin"] span[style*="font-family: 'Albert Sans'"],
body[data-page="admin"] span[style*="font-family: 'Albert Sans'"] *,
body[data-page="admin"] span[style*="font-family: 'Silkscreen'"],
body[data-page="admin"] span[style*="font-family: 'Silkscreen'"] *,
body[data-page="contact"] .btn-primary,
body[data-page="contact"] .btn-primary *,
body[data-page="contact"] button[type="submit"],
body[data-page="contact"] button[type="submit"] *,
button,
button *,
button.pg-chip,
button.pg-chip *,
button[class*="resolve-ip"],
button[class*="resolve-ip"] *,
button[onclick*="autoResolveAll"],
button[onclick*="autoResolveAll"] *,
button[onclick*="toggleBotFilter"],
button[onclick*="toggleBotFilter"] *,
button[onclick*="toggleCalibrationMode"],
button[onclick*="toggleCalibrationMode"] *,
button[type="submit"]:not(.btn-secondary-standard):not(.btn-danger-standard),
button[type="submit"]:not(.btn-secondary-standard):not(.btn-danger-standard) *,
div[style*="pagination"] a,
div[style*="pagination"] a *,
div[style*="pagination"] span,
div[style*="pagination"] span *,
footer:not(.server-info-footer) .footer [style*="Silkscreen"],
footer:not(.server-info-footer) .footer [style*="Silkscreen"] *,
footer:not(.server-info-footer) [style*="Silkscreen"],
footer:not(.server-info-footer) [style*="Silkscreen"] *,
footer:not(.site-footer):not(.server-info-footer) *,
footer:not(.site-footer):not(.server-info-footer) * *,
h1.filter-section-title,
h1.filter-section-title *,
h2.filter-section-title,
h2.filter-section-title *,
h3.filter-section-title,
h3.filter-section-title *,
h4.filter-section-title,
h4.filter-section-title *,
h5.filter-section-title,
h5.filter-section-title *,
h6.filter-section-title,
h6.filter-section-title *,
input[type="button"],
input[type="button"] *,
input[type="submit"],
input[type="submit"] *,
nav .nav-link,
nav .nav-link *,
select.pg-select,
select.pg-select *,
span.dropdown.header-controls-lang .dropdown-item,
span.dropdown.header-controls-lang .dropdown-item *,
span[style*="Albert Sans"][style*="min-width: 40px"],
span[style*="Albert Sans"][style*="min-width: 40px"] *,
span[style*="font-family: Silkscreen"],
span[style*="font-family: Silkscreen"] *,
span[style*="font-family:Silkscreen"],
span[style*="font-family:Silkscreen"] *,
table .badge,
table .badge *,
table .resolve-ip-btn,
table .resolve-ip-btn *,
table tbody td,
table tbody td *,
table thead th,
table thead th * {
    /* -moz-osx-font-smoothing has no `none`; grayscale is the closest
       Firefox on macOS offers. */
    -webkit-font-smoothing: none !important;
    -moz-osx-font-smoothing: grayscale !important;
    font-smooth: never !important;

    /* optimizeSpeed keeps glyph advances whole; geometricPrecision would do
       the opposite and position them on subpixels. Kerning is off for the
       same reason - a kern pair moves a glyph by a fraction of an em. */
    text-rendering: optimizeSpeed !important;
    font-kerning: none !important;
    font-variant-ligatures: none !important;
}

/* ------------------------------------------------------------------ *
 * Tracking, rounded to whole pixels
 * ------------------------------------------------------------------ */

/* 0 - 3 selectors */
.server-stat-item,
.time-current,
.time-total {
    letter-spacing: 0 !important;
}

/* 1px - 91 selectors */
.ab-profile-tagline,
.adm-btn,
.adm-label,
.adm-nav-item,
.adm-panel-note,
.adm-sub,
.adm-tab,
.adm-table th,
.adm-tag,
.admin-badge,
.article-category,
.article-container-dark .btn,
.article-container-light .btn,
.article-date,
.article-edit-button,
.article-featured-badge,
.article-meta,
.article-meta-item,
.article-read-more,
.article-tag,
.bulk-drop-note,
.bulk-status,
.card-badge span,
.card-tag,
.cv-exp-tags li,
.cv-exp-when,
.cv-exp-where,
.cv-skill-cat > h3,
.dt-fact-label,
.dt-figure-hint,
.dt-score-badge,
.dt-shot-cap,
.dt-tag,
.filter-btn,
.filter-section-title,
.footer-copy,
.footer-cta,
.footer-eyebrow,
.footer-legal a,
.footer-links a,
.footer-role,
.footer-status,
.footer-top,
.header-controls-lang .dropdown-item,
.header-controls-panel,
.header-controls-register,
.auth-label,
.home-btn,
.home-eyebrow,
.home-media-empty,
.home-meta,
.home-platform-marks .platform-badge-text,
.home-platforms-key,
.home-post-cat,
.home-post-date,
.home-section-link,
.home-stat-key,
.home-tag,
.intro-subtitle,
.lb-tag,
.lb-updated,
.map-stats .stats-header,
.mq-btn,
.mq-count,
.mu-system,
.mu-track.is-draft .mu-title::after,
.nav-tabs .nav-link,
.navbar .dropdown-menu .dropdown-item,
.navbar-nav .nav-link,
.page-chip,
.page-head-eyebrow,
.page-head-meta,
.page-legal-updated,
.page-section-note,
.page-subtitle,
.palette-group,
.pf-btn,
.pf-media-empty,
.pf-release,
.pf-tags li,
.pg-chip,
.pg-filter-label,
.platform-badge-text,
.playlist-empty,
.playlist-item-artist,
.playlist-item-title,
.playlist-note,
.playlist-reset,
.playlist-toggle-btn,
.re-lb-bar,
.re-search-all,
.re-search-group-label,
.rs-aside-label,
.rs-contact,
.rs-rec-role,
.rs-when,
.rs-where,
.saved-theme-btn,
.section-header-text .section-meta,
.section-header-text .section-subtitle,
.site-message-container .message-meta,
.site-message-dismiss,
.site-message-priority,
.sr-kind,
.theme-angle-presets button,
.theme-btn,
.theme-preset-btn span,
.theme-section h4,
.theme-toast button,
.track-artist,
.track-title,
.user-role-display,
[data-about-section] > .section-subtitle,
body > .dropdown-menu.header-controls-lang-portal .dropdown-item,
body[data-page="contact"] .btn-primary,
body[data-page="contact"] button[type="submit"],
button.pg-chip,
select.pg-select,
span.dropdown.header-controls-lang .dropdown-item,
table thead th {
    letter-spacing: 1px !important;
}

/* 2px - 1 selector */
.profile-tagline {
    letter-spacing: 2px !important;
}


/* ===== assets/css/theme-palettes.css ===== */
/**
 * Palette crossfade.
 * ==================
 *
 * A custom property normally cannot be animated. `--re-brand-400: #826b7d`
 * is just a token as far as the browser is concerned - a string it pastes
 * wherever the variable is used - so changing it repaints instantly and the
 * whole site snaps from one palette to the next.
 *
 * @property is what changes that. Registering a property with a `<color>`
 * syntax tells the browser it really is a colour, and a registered property
 * can be interpolated like any other animatable value. Once the ramp is
 * registered, one transition on :root crossfades every single thing that
 * derives from it - the ground gradient, panels, borders, accents, rules,
 * links - without any of them knowing a palette changed.
 *
 * The initial-value on each is Slate, the site's default palette. A browser
 * that has not been given one - no cookie, or the inline block in the head
 * missing - still gets the colours the site is supposed to wear, rather
 * than the mauve that now belongs to the Super Nintendo.
 *
 * Unregistered browsers (anything without @property) ignore these blocks
 * entirely and simply snap between palettes instead of fading. Nothing
 * breaks; the change just arrives all at once.
 */

@property --re-brand-900 { syntax: '<color>'; inherits: true; initial-value: #1c2025; }
@property --re-brand-800 { syntax: '<color>'; inherits: true; initial-value: #262c32; }
@property --re-brand-700 { syntax: '<color>'; inherits: true; initial-value: #3d444a; }
@property --re-brand-600 { syntax: '<color>'; inherits: true; initial-value: #4e555d; }
@property --re-brand-500 { syntax: '<color>'; inherits: true; initial-value: #5b646d; }
@property --re-brand-400 { syntax: '<color>'; inherits: true; initial-value: #6f777f; }
@property --re-brand-300 { syntax: '<color>'; inherits: true; initial-value: #848f99; }
@property --re-brand-200 { syntax: '<color>'; inherits: true; initial-value: #a2acb7; }
@property --re-brand-100 { syntax: '<color>'; inherits: true; initial-value: #cad0d6; }
@property --re-brand-050 { syntax: '<color>'; inherits: true; initial-value: #e7eaec; }

/* The one colour that is not on the ramp: the bright dot in the particle
   field. Registered too, so the canvas can read a fading value rather than a
   stepped one. */
@property --re-particle-pop { syntax: '<color>'; inherits: true; initial-value: #848bb8; }

/* ------------------------------------------------------------------ *
 * The fade itself
 * ------------------------------------------------------------------ */

:root {
    /*
     * One place to change how long a palette takes to arrive. The particle
     * canvas reads this same number out of the computed style so the
     * background and the page finish together rather than drifting apart -
     * see the palette tween in particle-network-interactive.js.
     */
    --re-palette-fade: 900ms;

    transition:
        --re-brand-900 var(--re-palette-fade) ease,
        --re-brand-800 var(--re-palette-fade) ease,
        --re-brand-700 var(--re-palette-fade) ease,
        --re-brand-600 var(--re-palette-fade) ease,
        --re-brand-500 var(--re-palette-fade) ease,
        --re-brand-400 var(--re-palette-fade) ease,
        --re-brand-300 var(--re-palette-fade) ease,
        --re-brand-200 var(--re-palette-fade) ease,
        --re-brand-100 var(--re-palette-fade) ease,
        --re-brand-050 var(--re-palette-fade) ease,
        --re-particle-pop var(--re-palette-fade) ease;
}

/*
 * The ground is painted on <body>, and it is a gradient rather than a flat
 * colour, so it does not inherit the interpolation for free - a gradient is
 * repainted from whatever the variables currently hold. That happens to be
 * exactly what we want: because the variables are themselves mid-transition,
 * the gradient is redrawn every frame with the interpolated values and the
 * ground fades along with everything else.
 */

@media (prefers-reduced-motion: reduce) {
    :root {
        --re-palette-fade: 1ms;
    }
}

/* ------------------------------------------------------------------ *
 * The picker
 * ------------------------------------------------------------------ */

/*
 * Fifteen palettes is taller than a short viewport, so the menu has to
 * scroll - and getting it to was not a one-liner.
 *
 * header-controls-panel.css sets `overflow: hidden !important` on the
 * portaled menu, which is right for the language picker it was written for:
 * five items, no scrolling, and the clip keeps the glass inside the rounded
 * corners. At `.dropdown-menu.header-controls-lang-portal` that scores
 * (0,2,0), so a plain `.palette-menu` at (0,1,0) lost even with !important
 * and the list was simply cut off at the bottom with no way to reach the
 * rest.
 *
 * Matching that specificity - and this file loads afterwards - is what makes
 * it scroll. overflow-x stays hidden so the corners still clip.
 */
.dropdown-menu.palette-menu {
    min-width: 196px;
    padding: 6px;
    max-height: min(70vh, 560px);
    overflow-x: hidden !important;
    overflow-y: auto !important;
    overscroll-behavior: contain;
}

/*
 * A visible scrollbar, because the alternative is a list that looks finished
 * when it is not. Thin and in the palette rather than the browser's default
 * slab, which on a dark glass panel reads as a rendering fault.
 */
.dropdown-menu.palette-menu {
    scrollbar-width: thin;
    scrollbar-color: var(--re-brand-400) transparent;
}

.dropdown-menu.palette-menu::-webkit-scrollbar { width: 8px; }
.dropdown-menu.palette-menu::-webkit-scrollbar-track { background: transparent; }

.dropdown-menu.palette-menu::-webkit-scrollbar-thumb {
    background: var(--re-surface-hover);
    border: 2px solid transparent;
    background-clip: content-box;
    border-radius: 8px;
}

.dropdown-menu.palette-menu::-webkit-scrollbar-thumb:hover {
    background: var(--re-brand-400);
    background-clip: content-box;
}

/* The group heading. Not a menu item - it is not clickable and should not
   look like one. */
.palette-group {
    padding: 10px 8px 4px;
    font-family: var(--re-font-ui);
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--re-brand-200);
    opacity: 0.6;
}

.palette-group:not(:first-child) {
    margin-top: 4px;
    border-top: 1px solid var(--re-border-faint);
}

.palette-item {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    /* 5px rather than 6: fifteen rows, and the two saved pixels each are a
       whole extra palette visible before the list needs scrolling. */
    padding: 5px 8px;
    border: 1px solid transparent;
    border-radius: var(--re-radius-sm);
    background: none;
    color: var(--re-text-color);
    text-align: left;
    cursor: pointer;
    transition: background var(--re-transition), border-color var(--re-transition);
}

.palette-item:hover,
.palette-item:focus-visible {
    background: var(--re-surface-hover);
    border-color: var(--re-border);
}

.palette-item[aria-current="true"] {
    border-color: var(--re-border-strong);
    background: var(--re-surface-raised);
}

/*
 * The swatch is the palette in miniature - its ground, its accent and its
 * bright step - rather than a single dot. Three colours are what actually
 * distinguish two dark themes from each other; one is not enough to tell
 * Indigo from Cobalt.
 */
.palette-swatch {
    flex: 0 0 auto;
    width: 26px;
    height: 14px;
    border-radius: 3px;
    border: 1px solid var(--re-border-faint);
    background: linear-gradient(
        to right,
        var(--sw-ground) 0 34%,
        var(--sw-accent) 34% 67%,
        var(--sw-bright) 67% 100%);
}

.palette-name {
    display: block;
    font-family: var(--re-font-ui);
    line-height: 1.4;
}

.palette-note {
    display: block;
    font-family: var(--re-font-ui);
    opacity: 0.5;
    line-height: 1.4;
}

/* The follow-the-music switch, sitting under the list. */
.palette-follow {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 6px;
    padding: 8px;
    border-top: 1px solid var(--re-border-faint);
    color: var(--re-text-color);
    font-family: var(--re-font-ui);
    cursor: pointer;
}

.palette-follow input {
    accent-color: var(--re-brand-300);
    margin: 0;
}

/*
 * The trigger sits in the header controls strip beside the language picker
 * and is built to match it exactly - see .header-controls-lang-toggle in
 * header-controls-panel.css. The 34px floor is the mobile tap target the
 * rest of that strip uses.
 */
.header-controls-palette-toggle {
    color: var(--header-controls-fg) !important;
    text-decoration: none;
    padding: 6px 10px;
    background: var(--re-surface-raised);
    border: 1px solid var(--re-border);
    border-radius: var(--re-radius-sm);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: all 0.3s ease;
    cursor: pointer;
}

.header-controls-palette-toggle:hover {
    background: var(--re-surface-hover);
    border-color: var(--re-border-strong);
    color: var(--bs-link-hover-color) !important;
}

@media (max-width: 768px) {
    .header-controls-palette-toggle {
        min-height: 34px;
    }
    /* The name is the first thing to go when the row runs out of width; the
       swatch still says which palette is on. */
    .header-controls-palette-toggle .palette-current-name {
        display: none;
    }
}

@media (min-width: 769px) {
    .header-controls-palette-toggle {
        min-height: 28px;
    }
}

