/* ============================================================
   CUB // CORRUPTED  —  testing.cubsoftware.site
   ============================================================
   A professional software platform that has been running too
   long, in a building nobody has visited for a while. The
   horror is in the restraint: near-black surfaces, hairline
   borders, monospace system labels, and one red used sparingly
   enough that it still means something when it appears.

   MECHANISM
   This file loads AFTER shared.css and any page CSS on every
   page served from testing.cubsoftware.site, and redefines the
   same custom properties the whole site already uses. All 128
   templates inherit it without a single production file being
   touched. See _inject_testing_theme in main.py.

   DELIBERATELY ABSENT
   No audio anywhere, and no film grain, static or VHS noise
   overlay — "no noise" was an explicit instruction. The only
   atmosphere layers are a vignette and a very faint scanline,
   both structural rather than noisy, and both switched off by
   prefers-reduced-motion. To reintroduce grain later, that is
   what .cub-grain is reserved for; nothing renders it today.

   RULE OF THUMB FOR ANYTHING ADDED HERE
   Usability first. If an effect makes text harder to read, or
   moves something the user is trying to click, it is wrong no
   matter how good it looks.
   ============================================================ */

:root {
    /* ── the world ──────────────────────────────────────────
       Charcoals with a blue-green bias, not neutral grey. A
       pure grey reads as unfinished; this reads as a room with
       the lights off. */
    --cub-void:        #08090b;   /* page ground                    */
    --cub-panel:       #0e1013;   /* cards, surfaces                */
    --cub-raised:      #15181d;   /* hovered / elevated             */
    --cub-line:        #1e2228;   /* hairline borders               */
    --cub-line-warm:   #2a1d20;   /* borders near something wrong   */

    /* ── the accent ─────────────────────────────────────────
       One red, used for primary actions and system alerts and
       almost nowhere else. It stops meaning anything the moment
       it is painted onto every border. */
    --cub-red:         #a50f12;
    --cub-red-bright:  #d10a10;
    --cub-red-dim:     #5c0508;
    --cub-red-ghost:   rgba(165, 15, 18, 0.09);

    --cub-text:        #dfe2e6;   /* off-white, never #fff          */
    --cub-text-dim:    #98a0aa;
    --cub-muted:       #6b737e;
    --cub-green:       #3f9e63;   /* ACTIVE / NOMINAL only          */
    --cub-amber:       #b98b2e;

    --cub-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    --cub-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;

    /* ── the site's own variables, redefined ────────────────
       Everything already written against these picks up the
       theme with no further work. This is why the redesign
       reaches pages nobody has looked at. */
    --primary-color:    var(--cub-red);
    --primary:          var(--cub-red);
    --primary-dark:     var(--cub-red-dim);
    --secondary-color:  #6d2029;
    --secondary:        #6d2029;
    --accent-color:     var(--cub-red);
    --bg-dark:          var(--cub-void);
    --dark-bg:          var(--cub-void);
    --bg-primary:       var(--cub-void);
    --bg-secondary:     var(--cub-panel);
    --background-color: var(--cub-void);
    --surface-color:    var(--cub-panel);
    --surface-hover:    var(--cub-raised);
    --card-bg:          var(--cub-panel);
    --text-primary:     var(--cub-text);
    --text-light:       var(--cub-text);
    --text-secondary:   var(--cub-text-dim);
    --text-muted:       var(--cub-muted);
    --border-color:     var(--cub-line);
    --border-hover:     var(--cub-line-warm);
    --success:          var(--cub-green);
    --error:            var(--cub-red-bright);
    --danger:           var(--cub-red-bright);
    --warning:          var(--cub-amber);
    --info:             #4a6b8a;
}

/* ── base ───────────────────────────────────────────────── */

html, body {
    background: var(--cub-void);
    color: var(--cub-text);
    font-family: var(--cub-sans);
    -webkit-font-smoothing: antialiased;
}

body {
    /* A vignette, not noise: the page edges fall away so the
       content sits in a pool of light. Fixed so it does not
       scroll, and behind everything. */
    background-image:
        radial-gradient(120% 90% at 50% 0%,
            rgba(30, 34, 42, 0.55) 0%, transparent 60%),
        radial-gradient(100% 100% at 50% 100%,
            rgba(200, 32, 46, 0.025) 0%, transparent 55%);
    background-attachment: fixed;
}

h1, h2, h3, h4, h5 { color: var(--cub-text); letter-spacing: -0.01em; }
h1, h2 { text-wrap: balance; }
a { color: var(--cub-red-bright); }
a:hover { color: #ff5563; }

::selection { background: var(--cub-red-dim); color: #fff; }

/* Focus must stay obvious — a dark theme is exactly where a
   faint focus ring disappears entirely. */
:focus-visible {
    outline: 2px solid var(--cub-red-bright);
    outline-offset: 2px;
}

/* ── system typography ──────────────────────────────────────
   Monospace is reserved for things the "system" is saying:
   statuses, file numbers, build strings, timestamps. Using it
   for body copy would make the whole site cosplay as a
   terminal, which is the thing the brief warns against. */

.cub-sys,
.cub-file-no,
.cub-meta-key,
.cub-meta-val,
.cub-status,
.cub-sector-no {
    font-family: var(--cub-mono);
    font-size: 0.7rem;
    letter-spacing: 0.11em;
    text-transform: uppercase;
}

.cub-sys { color: var(--cub-muted); }

/* ── surfaces ───────────────────────────────────────────── */

.settings-card,
.feature-card,
.pm-glass-panel,
.dev-card,
.stat-card,
.category-section > .features-grid > * {
    background: var(--cub-panel);
    border: 1px solid var(--cub-line);
    border-radius: 4px;                 /* nearly square: institutional, not friendly */
    box-shadow: none;
    backdrop-filter: none;
    transition: border-color 160ms linear, background-color 160ms linear;
}

.settings-card:hover,
.feature-card:hover {
    background: var(--cub-raised);
    border-color: var(--cub-line-warm);
}

/* ── file cards ─────────────────────────────────────────────
   Product cards become system files. The real product name
   stays the loudest thing in the card — the file number is
   set small and dim, because navigation clarity outranks lore. */

.feature-card {
    position: relative;
    padding-top: 2.35rem;
    overflow: hidden;
}

/* FILE // 001 — injected by testing.js as a data attribute so
   no template is edited. */
.feature-card[data-cub-file]::before {
    content: 'FILE // ' attr(data-cub-file);
    position: absolute;
    top: 0.7rem;
    left: 1.15rem;
    font-family: var(--cub-mono);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    color: var(--cub-muted);
}

/* the hairline under the file number */
.feature-card[data-cub-file]::after {
    content: '';
    position: absolute;
    top: 2rem;
    left: 1.15rem;
    right: 1.15rem;
    height: 1px;
    background: var(--cub-line);
}

.feature-card h3 {
    font-size: 1.02rem;
    letter-spacing: 0.005em;
    margin-bottom: 0.3rem;
}

.feature-card p { color: var(--cub-text-dim); line-height: 1.55; }

/* The status row testing.js appends. */
.cub-file-meta {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.15rem 0.75rem;
    margin-top: 0.9rem;
    padding-top: 0.7rem;
    border-top: 1px solid var(--cub-line);
}

.cub-meta-key { color: var(--cub-muted); }
.cub-meta-val { color: var(--cub-text-dim); }

.cub-status { display: inline-flex; align-items: center; gap: 0.4rem; }
.cub-status::before {
    content: '';
    width: 5px; height: 5px;
    border-radius: 50%;
    background: var(--cub-green);
    box-shadow: 0 0 6px currentColor;
    color: var(--cub-green);
}
.cub-status[data-state="restricted"] { color: var(--cub-red-bright); }
.cub-status[data-state="restricted"]::before { background: var(--cub-red-bright); color: var(--cub-red-bright); }
.cub-status[data-state="unknown"] { color: var(--cub-muted); }
.cub-status[data-state="unknown"]::before { background: var(--cub-muted); color: var(--cub-muted); }

/* Corner ticks — cheap, and they make a flat rectangle read as
   a panel in a rack rather than a web card. */
.feature-card:hover::after { background: var(--cub-red-dim); }

/* ── the badge on each card ─────────────────────────────── */

.feature-badge {
    font-family: var(--cub-mono);
    font-size: 0.62rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    background: transparent;
    border: 1px solid var(--cub-line-warm);
    color: var(--cub-text-dim);
    border-radius: 2px;
    padding: 0.25rem 0.5rem;
}

/* ── sectors ────────────────────────────────────────────────
   Category headings become sectors. The human-readable name is
   kept at full weight; the sector number is the decoration. */

.category-title {
    font-family: var(--cub-sans);
    font-size: 0.95rem;
    letter-spacing: 0.02em;
    color: var(--cub-text);
    display: flex;
    align-items: baseline;
    gap: 0.7rem;
    padding-bottom: 0.55rem;
    border-bottom: 1px solid var(--cub-line);
}

.category-title[data-cub-sector]:not([data-cub-anchored])::before {
    content: 'SECTOR ' attr(data-cub-sector) ' //';
    font-family: var(--cub-mono);
    font-size: 0.64rem;
    letter-spacing: 0.18em;
    color: var(--cub-red-dim);
    flex: 0 0 auto;
}

/* ── buttons ────────────────────────────────────────────── */

.btn-primary, .control-btn.primary, .cta-button, .btn-withdraw, .playground-btn {
    background: var(--cub-red);
    border: 1px solid var(--cub-red);
    color: #fff;
    border-radius: 3px;
    font-family: var(--cub-mono);
    font-size: 0.72rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    transition: background-color 140ms linear, border-color 140ms linear;
}
.btn-primary:hover, .control-btn.primary:hover, .cta-button:hover, .btn-withdraw:hover {
    background: var(--cub-red-bright);
    border-color: var(--cub-red-bright);
}

.btn-secondary, .control-btn, .feature-card-link .feature-badge {
    background: transparent;
    border: 1px solid var(--cub-line);
    color: var(--cub-text-dim);
    border-radius: 3px;
}
.control-btn:hover { border-color: var(--cub-line-warm); color: var(--cub-text); }

/* ── forms ──────────────────────────────────────────────── */

.form-input, .form-select, input[type="text"], input[type="number"],
input[type="search"], input[type="url"], textarea, select {
    background: #0a0c0f;
    border: 1px solid var(--cub-line);
    color: var(--cub-text);
    border-radius: 3px;
}
.form-input:focus, .form-select:focus, input:focus, textarea:focus, select:focus {
    border-color: var(--cub-red-dim);
    outline: none;
    box-shadow: 0 0 0 1px var(--cub-red-ghost);
}
::placeholder { color: #1e2228; }

/* ── navigation ─────────────────────────────────────────── */

header, .navbar, .site-header, .sidebar {
    background: rgba(8, 9, 11, 0.92);
    border-bottom: 1px solid var(--cub-line);
    backdrop-filter: blur(6px);
}

.nav-item, .nav-link {
    color: var(--cub-text-dim);
    border-radius: 3px;
}
.nav-item:hover, .nav-link:hover { color: var(--cub-text); background: var(--cub-raised); }
.nav-item.active { color: var(--cub-text); background: var(--cub-raised); }
.nav-item.active::before {
    /* a thin red rail rather than a filled pill */
    content: '';
    position: absolute;
    left: 0; top: 20%; bottom: 20%;
    width: 2px;
    background: var(--cub-red);
}
.nav-item { position: relative; }

.nav-section-title {
    font-family: var(--cub-mono);
    font-size: 0.6rem;
    letter-spacing: 0.2em;
    color: #1e2228;
}

/* ── tables ─────────────────────────────────────────────── */

.pm-table, table { border-color: var(--cub-line); }
.pm-th, th {
    font-family: var(--cub-mono);
    font-size: 0.64rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--cub-muted);
    border-bottom: 1px solid var(--cub-line);
}
td { border-bottom: 1px solid #14171b; color: var(--cub-text-dim); }
tr:hover td { background: rgba(255, 255, 255, 0.014); }

/* ── hero ───────────────────────────────────────────────── */

.hero, .dev-hero, .hero-section {
    background: transparent;
    border-bottom: 1px solid var(--cub-line);
}

.hero h1, .hero-title {
    font-weight: 700;
    letter-spacing: -0.025em;
}

/* The system line under the hero. Deliberately quiet. */
.cub-system-line {
    font-family: var(--cub-mono);
    font-size: 0.66rem;
    letter-spacing: 0.18em;
    color: var(--cub-muted);
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin-top: 1.4rem;
}
.cub-system-line b { color: var(--cub-green); font-weight: 500; }
.cub-system-line[data-corrupt="1"] b { color: var(--cub-red-bright); }

/* ── atmosphere ─────────────────────────────────────────────
   Two layers, both pointer-events:none, both fixed, both far
   below the threshold where they cost readability. No grain,
   no static: see the header note. */

.cub-atmos {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 9998;
}

/* Scanline: 3px period, 2% black. At this strength it reads as
   a screen rather than a pattern, and it does not moire against
   body text at any zoom level worth supporting. */
.cub-scanlines {
    background: repeating-linear-gradient(
        to bottom,
        rgba(0, 0, 0, 0.20) 0px,
        rgba(0, 0, 0, 0.20) 1px,
        transparent 1px,
        transparent 3px
    );
    opacity: 0.16;
}

/* Edge darkening. Keeps the middle of the page clean. */
.cub-vignette {
    background: radial-gradient(120% 80% at 50% 45%,
        transparent 55%, rgba(0, 0, 0, 0.55) 100%);
}

/* Reserved. Nothing renders this — kept so grain has one
   obvious home if it is ever wanted, rather than being
   sprinkled through components. */
.cub-grain { display: none !important; }

/* ── boot ───────────────────────────────────────────────────
   Short, skippable, and shown once per session — never a gate
   in front of someone who came to download a tool. */

.cub-boot {
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: var(--cub-void);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--cub-mono);
    font-size: 0.76rem;
    letter-spacing: 0.14em;
    color: var(--cub-text-dim);
    transition: opacity 260ms linear;
}
.cub-boot[hidden] { display: none; }
.cub-boot.is-done { opacity: 0; pointer-events: none; }
.cub-boot-lines { min-width: 17rem; }
.cub-boot-lines div { opacity: 0; animation: cub-boot-in 140ms linear forwards; }
.cub-boot-lines div:nth-child(1) { animation-delay: 40ms; }
.cub-boot-lines div:nth-child(2) { animation-delay: 200ms; }
.cub-boot-lines div:nth-child(3) { animation-delay: 340ms; }
.cub-boot-lines div:nth-child(4) { animation-delay: 470ms; }
.cub-boot-lines div b { color: var(--cub-green); font-weight: 400; }
@keyframes cub-boot-in { to { opacity: 1; } }

.cub-boot-skip {
    position: absolute;
    bottom: 2rem;
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    color: #1e2228;
    background: none;
    border: 0;
    cursor: pointer;
}
.cub-boot-skip:hover { color: var(--cub-text-dim); }

/* ── terminal ───────────────────────────────────────────── */

.cub-term {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    width: min(30rem, calc(100vw - 2.5rem));
    height: min(24rem, 60vh);
    z-index: 9999;
    display: flex;
    flex-direction: column;
    background: rgba(8, 9, 11, 0.985);
    border: 1px solid var(--cub-line-warm);
    border-radius: 4px;
    font-family: var(--cub-mono);
    font-size: 0.74rem;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.7);
}
.cub-term[hidden] { display: none; }

.cub-term-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 0.7rem;
    border-bottom: 1px solid var(--cub-line);
    color: var(--cub-muted);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
}
.cub-term-bar button {
    background: none; border: 0; cursor: pointer;
    color: var(--cub-muted); font-family: inherit; font-size: 0.8rem;
}
.cub-term-bar button:hover { color: var(--cub-text); }

.cub-term-out {
    flex: 1;
    overflow-y: auto;
    padding: 0.7rem;
    white-space: pre-wrap;
    word-break: break-word;
    color: var(--cub-text-dim);
    line-height: 1.6;
}
.cub-term-out .ok   { color: var(--cub-green); }
.cub-term-out .warn { color: var(--cub-amber); }
.cub-term-out .bad  { color: var(--cub-red-bright); }
.cub-term-out .cmd  { color: var(--cub-text); }

.cub-term-in {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    border-top: 1px solid var(--cub-line);
    padding: 0.5rem 0.7rem;
}
.cub-term-in span { color: var(--cub-red); }
.cub-term-in input {
    flex: 1;
    background: none;
    border: 0;
    outline: none;
    color: var(--cub-text);
    font-family: inherit;
    font-size: inherit;
}

/* ── incident archive & restricted sector ───────────────── */

.cub-incident {
    border: 1px solid var(--cub-line);
    border-left: 2px solid var(--cub-red-dim);
    background: var(--cub-panel);
    border-radius: 3px;
    padding: 1rem 1.15rem;
    margin-bottom: 0.75rem;
}
.cub-incident h4 {
    font-family: var(--cub-mono);
    font-size: 0.72rem;
    letter-spacing: 0.14em;
    color: var(--cub-text);
    margin: 0 0 0.5rem;
}
.cub-incident-meta {
    font-family: var(--cub-mono);
    font-size: 0.62rem;
    letter-spacing: 0.12em;
    color: var(--cub-muted);
    margin-bottom: 0.6rem;
}
.cub-incident p { color: var(--cub-text-dim); line-height: 1.65; margin: 0 0 0.5rem; }
.cub-incident.is-locked { opacity: 0.5; }

/* Redacted text: a block you can select but not read, which is
   better than an image because it stays copy-pasteable-safe and
   screen readers get the real label from aria-label. */
.cub-redact {
    background: #15181d;
    color: transparent;
    border-radius: 1px;
    padding: 0 0.15em;
    user-select: none;
}

/* ── mascot appearances ─────────────────────────────────────
   The asset is never modified — it is placed in near-darkness
   at low opacity, which is where the glowing eyes do the work. */

.cub-mascot {
    position: fixed;
    pointer-events: none;
    z-index: 9997;
    opacity: 0;
    filter: brightness(0.42) saturate(0.75) contrast(1.05);
    transition: opacity 1400ms ease-in-out;
    width: 190px;
    max-width: 34vw;
}
.cub-mascot.is-visible { opacity: 0.17; }

/* ── footer ─────────────────────────────────────────────── */

footer, .site-footer {
    background: transparent;
    border-top: 1px solid var(--cub-line);
    color: var(--cub-muted);
}
.cub-foot-sys {
    font-family: var(--cub-mono);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    color: #1e2228;
    margin-top: 0.5rem;
}

/* ── dev panel (only ever injected when ?cubdev=1) ──────── */

.cub-dev {
    position: fixed;
    left: 1rem; bottom: 1rem;
    z-index: 10001;
    background: rgba(8, 9, 11, 0.98);
    border: 1px solid var(--cub-line-warm);
    border-radius: 4px;
    padding: 0.75rem;
    font-family: var(--cub-mono);
    font-size: 0.66rem;
    color: var(--cub-text-dim);
    max-width: 15rem;
}
.cub-dev h5 { margin: 0 0 0.5rem; color: var(--cub-red-bright); font-size: 0.62rem; letter-spacing: 0.16em; }
.cub-dev button {
    display: block; width: 100%; text-align: left;
    background: none; border: 1px solid var(--cub-line); border-radius: 2px;
    color: var(--cub-text-dim); font-family: inherit; font-size: 0.63rem;
    padding: 0.25rem 0.4rem; margin-bottom: 0.25rem; cursor: pointer;
}
.cub-dev button:hover { border-color: var(--cub-red-dim); color: var(--cub-text); }

/* ── mobile ─────────────────────────────────────────────────
   The experience adapts rather than being switched off: the
   terminal becomes a sheet, the mascot shrinks, nothing depends
   on hover. */

@media (max-width: 640px) {
    .cub-term {
        right: 0; left: 0; bottom: 0;
        width: 100%;
        height: min(70vh, 30rem);
        border-radius: 4px 4px 0 0;
        border-left: 0; border-right: 0; border-bottom: 0;
    }
    .cub-mascot { width: 130px; }
    .category-title { flex-wrap: wrap; gap: 0.35rem; }
    .cub-scanlines { opacity: 0.1; }
}

/* ── reduced motion ─────────────────────────────────────────
   Everything atmospheric is motion. With this on, the theme
   keeps its palette and typography and drops every moving or
   flickering part — including the boot sequence, which is pure
   animation and would otherwise be a delay for no benefit. */

@media (prefers-reduced-motion: reduce) {
    .cub-scanlines { display: none; }
    .cub-boot { display: none !important; }
    .cub-mascot { display: none; }
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
    }
}

/* ── print ──────────────────────────────────────────────── */

@media print {
    .cub-atmos, .cub-term, .cub-boot, .cub-mascot, .cub-dev { display: none !important; }
    html, body { background: #fff; color: #000; }
}

/* ═══════════════════════════════════════════════════════════
   THE WORDMARK — corrupted
   ═══════════════════════════════════════════════════════════
   Targets .cx-brand b, the actual wordmark on this host — the
   testing site has its own header (.cx-top), not the production
   .logo/.logo-text markup.

   No corrupted typeface is loaded and font-src is 'self', so a
   display face cannot be pulled in from anywhere. The corruption
   is built instead, on the mono face already here — which suits
   it better anyway: this should read as a system rendering the
   name wrong, not as a horror font someone chose.

   Static by default. A wordmark that never stops moving stops
   being a wordmark, and it sits in the header of every page. The
   movement lives in testing.js on the ambient scheduler, and it
   is rare on purpose. */

.cx-brand b {
    position: relative;
    font-family: var(--cub-mono, 'JetBrains Mono', ui-monospace, monospace);
    font-weight: 700;
    /* Condensed, so the mono face does not read as wide and
       friendly — the name should feel compressed. */
    transform: scaleX(0.93);
    transform-origin: left center;
    letter-spacing: .05em;
    color: #f2f4f6;
    isolation: isolate;
    /* Mono is fixed-width, so swapping letters alone shifts nothing —
       but a block glyph the face lacks falls back to another font with
       different metrics, and that would nudge the nav. Reserve the width. */
    display: inline-block;
    min-width: 11ch;
}

/* Chromatic split, in this site's own palette rather than the
   stock cyan/magenta — that pairing is why most glitch text looks
   like every other glitch text. Blood red against a cold shadow
   is the palette this host already speaks in. Both layers are
   decorative; ::before/::after are not in the accessibility tree,
   so the name stays intact for a screen reader. */
.cx-brand b::before,
.cx-brand b::after {
    content: attr(data-cub-text);
    position: absolute;
    left: 0;
    top: 0;
    pointer-events: none;
    z-index: -1;
    white-space: pre;
    transition: transform .12s steps(2), opacity .12s steps(2);
}

.cx-brand b::before {
    color: #d10a10;
    transform: translate(-1.5px, .5px);
    opacity: .6;
    clip-path: polygon(0 0, 100% 0, 100% 47%, 0 47%);
}

.cx-brand b::after {
    color: #2b6a7a;                     /* cold side of the split */
    transform: translate(1.5px, -.5px);
    opacity: .42;
    clip-path: polygon(0 53%, 100% 53%, 100% 100%, 0 100%);
}

.cx-brand:hover b::before { transform: translate(-2.5px, .5px); opacity: .8; }
.cx-brand:hover b::after  { transform: translate(2.5px, -.5px); opacity: .6; }

/* Mid-glitch — driven by testing.js, a few hundred ms at a time. */
.cx-brand b.is-corrupting          { animation: cubWordmarkShift .18s steps(2) infinite; }
.cx-brand b.is-corrupting::before  { transform: translate(-4px, 1px); opacity: .95; }
.cx-brand b.is-corrupting::after   { transform: translate(4px, -1px); opacity: .8; }

@keyframes cubWordmarkShift {
    0%   { transform: scaleX(0.93) translateX(0); }
    50%  { transform: scaleX(0.93) translateX(-1.5px); }
    100% { transform: scaleX(0.93) translateX(1px); }
}

/* Usability outranks atmosphere, which is the rule this whole
   theme is built on. Reduced motion keeps the treatment and drops
   the movement — still corrupted, just holding still. */
@media (prefers-reduced-motion: reduce) {
    .cx-brand b.is-corrupting { animation: none; }
    .cx-brand b::before,
    .cx-brand b::after { transition: none; }
}


/* The sector number, as a link to its own section — so it can be clicked,
   copied and shared. It was a ::before, which can be none of those. */
.cub-sector-tag {
    flex: 0 0 auto;
    font-family: var(--cub-mono);
    font-size: 0.64rem;
    letter-spacing: 0.18em;
    color: var(--cub-red-dim);
    text-decoration: none;
    transition: color .14s ease;
}

.cub-sector-tag:hover,
.cub-sector-tag:focus-visible { color: var(--cub-red-bright); }
.cub-sector-tag:focus-visible { outline: 1px solid var(--cub-red); outline-offset: 3px; }

/* Land the heading clear of any sticky chrome when jumped to. */
.category-title[id] { scroll-margin-top: 84px; }


/* ═══════════════════════════════════════════════════════════
   THE SHARED HEADER
   ═══════════════════════════════════════════════════════════
   Lifted out of the homepage's inline <style> so every themed page can
   use the same bar. The markup is injected server-side by
   _inject_testing_theme(), so no template needed editing.

   The site's original header is hidden rather than restyled: two headers
   stacked is worse than one, and the old one carries its own nav. */
.site-header, header.site-header, .navbar { display: none !important; }
body > .cx-top { margin-bottom: 1rem; }

.cx-top{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:2rem;
  padding:.7rem 1.1rem;border-bottom:1px solid var(--line);position:sticky;top:0;z-index:60;
  background:rgba(6,7,9,.96);backdrop-filter:blur(8px)}
.cx-top>.cx-brand{justify-self:start}
.cx-top>.cx-tbtn{justify-self:end}
.cx-brand{display:flex;align-items:center;gap:.65rem;flex:0 0 auto}
/* The brand was too dim to read: a desaturated logo next to 1.04rem text at
   #e8ebee, with the subtitle at .56rem in near-black red. Both lifted. */
.cx-brand img{width:36px;height:36px;object-fit:contain;filter:saturate(.55) brightness(1.05)}
.cx-brand b{display:block;font-size:1.22rem;font-weight:700;letter-spacing:.07em;color:#fff;line-height:1.15}
.cx-brand i{display:block;font-style:normal;font-size:.62rem;letter-spacing:.2em;color:#e8121a;
  text-shadow:0 0 14px rgba(232,18,26,.5)}

/* Nav: bigger, brighter, evenly spaced, and vertically centred against the
   brand instead of floating. The underline only appears on the active item. */
.cx-nav{display:flex;align-items:center;gap:2.4rem;justify-self:center;
  font-size:.85rem;letter-spacing:.11em;font-weight:700}
.cx-nav a{color:#aeb5bd;padding:.35rem 0;border-bottom:2px solid transparent;transition:color .14s}
.cx-nav a:hover{color:#fff}
.cx-nav a.on{color:#e8121a;border-bottom-color:var(--red)}
.cx-tbtn{font-family:inherit;font-size:.68rem;letter-spacing:.14em;color:var(--tx-d);
  border:1px solid var(--line);border-radius:2px;padding:.5rem .8rem;background:none;cursor:pointer}
.cx-tbtn:hover{border-color:var(--red-d);color:var(--red-b)}

/* The shared account menu, restyled to belong here. nav_auth.html positions
   itself fixed on pages with their own headers; inside this nav it has to sit
   in the row like any other item. */
.cx-acct .nav-auth{position:static;top:auto;right:auto;z-index:auto}
.cx-acct .nav-dropdown{position:relative}
.cx-acct .nav-link,.cx-acct .nav-login-btn{
  display:inline-flex;align-items:center;gap:.35rem;
  font-family:var(--mono);font-size:.85rem;letter-spacing:.11em;font-weight:700;
  text-transform:uppercase;color:#aeb5bd;background:none;border:0;padding:.35rem 0;
  cursor:pointer;transition:color .14s}
.cx-acct .nav-link:hover,.cx-acct .nav-login-btn:hover{color:#fff}
.cx-acct .dropdown-menu{
  position:absolute;right:0;top:100%;margin-top:.55rem;min-width:15rem;
  background:rgba(8,9,12,.985);border:1px solid var(--line-r);border-radius:2px;
  box-shadow:0 18px 50px -12px rgba(0,0,0,.9),0 0 0 1px rgba(140,8,14,.25);
  padding:.35rem 0;display:none;z-index:80;
  max-height:70vh;overflow-y:auto}
/* Opened by the shared script's `.open`, and by hover for pointer users. */
/* The bridge. Invisible, sits in the gap, and belongs to the dropdown — so
   the pointer stays inside it while travelling from the button to the menu. */
.cx-acct .nav-dropdown::after{
  content:'';position:absolute;right:0;top:100%;width:15rem;height:.75rem;
  display:none}
.cx-acct .nav-dropdown:hover::after,
.cx-acct .nav-dropdown.open::after{display:block}
.cx-acct .nav-dropdown:hover .dropdown-menu,
.cx-acct .nav-dropdown.open .dropdown-menu{display:block}
/* Keeps it open while the pointer is anywhere inside the menu itself. */
.cx-acct .dropdown-menu:hover{display:block}
.cx-acct .dropdown-menu a{
  display:block;padding:.48rem .85rem;font-size:.72rem;letter-spacing:.06em;
  color:var(--tx-d);border-left:2px solid transparent}
.cx-acct .dropdown-menu a:hover{background:var(--panel2);color:#fff;border-left-color:var(--red)}
.cx-acct .dropdown-divider{height:1px;background:var(--line);margin:.35rem 0}
.cx-acct .dropdown-section-label{
  display:block;padding:.4rem .85rem .2rem;font-size:.56rem;letter-spacing:.18em;
  text-transform:uppercase;color:var(--mut)}
.cx-acct .dropdown-danger{color:var(--red-b)!important}
.cx-acct .dropdown-danger:hover{background:rgba(140,8,14,.18)!important}
@media (max-width:860px){ .cx-acct{display:none} }

/* ── grid ────────────────────────────────────────────── */

/* ═══════════════════════════════════════════════════════════
   THE SHARED PRIMITIVES
   ═══════════════════════════════════════════════════════════
   The overlay covered 27 of the 2,615 classes these 129 templates use, which
   is why the tools still looked like the old site with a dark background.

   These are the generic pieces — wrappers, forms, result panels, buttons,
   toasts — that nearly every tool is built from. Styling them here themes the
   whole estate without touching a single template. Page-specific classes
   (calculator keys, dice pips, syntax tokens) are handled further down or left
   alone deliberately: a tool that has a considered look of its own should keep
   it. Only colour, border and spacing are touched — never layout that a page
   depends on to function. */

/* ── the page itself ─────────────────────────── */
.main-container, .container, .page-wrap, .content-wrap {
    background: transparent;
}

/* ── forms ───────────────────────────────────── */
.form-group { margin-bottom: 1rem; }

.form-group > label,
.settings-group > label,
label.form-label {
    display: block;
    margin-bottom: .4rem;
    font-family: var(--cub-mono);
    font-size: .68rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--cub-text-dim);
}

input[type="text"], input[type="url"], input[type="number"],
input[type="search"], input[type="email"], input[type="password"],
textarea, select,
.form-input, .form-select, .form-control {
    background: var(--cub-panel);
    border: 1px solid var(--cub-line);
    border-radius: 2px;
    color: var(--cub-text);
    font-family: inherit;
    transition: border-color .14s ease, background .14s ease;
}

input:focus, textarea:focus, select:focus,
.form-input:focus, .form-select:focus, .form-control:focus {
    outline: none;
    border-color: var(--cub-red);
    background: var(--cub-raised);
}

::placeholder { color: var(--cub-muted); opacity: 1; }

/* ── result panels ───────────────────────────── */
.result-card, .result-box, .output-card, .settings-group {
    background: var(--cub-panel);
    border: 1px solid var(--cub-line);
    border-radius: 2px;
}

.result-label, .rule-key {
    font-family: var(--cub-mono);
    font-size: .66rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--cub-muted);
}

.result-value, .rule-val {
    color: var(--cub-text);
    font-variant-numeric: tabular-nums;
}

.rule-row {
    display: flex;
    justify-content: space-between;
    gap: .8rem;
    padding: .35rem 0;
    border-bottom: 1px solid var(--cub-line);
}

.rule-row:last-child { border-bottom: 0; }

/* ── buttons ─────────────────────────────────── */
.btn, .action-btn, .live-btn, .theme-btn, .calc-btn {
    background: var(--cub-panel);
    border: 1px solid var(--cub-line);
    border-radius: 2px;
    color: var(--cub-text);
    font-family: var(--cub-mono);
    letter-spacing: .08em;
    transition: border-color .14s ease, background .14s ease, color .14s ease;
}

.btn:hover, .action-btn:hover, .live-btn:hover,
.theme-btn:hover, .calc-btn:hover {
    border-color: var(--cub-red);
    background: var(--cub-raised);
    color: #fff;
}

.btn.primary, .action-btn.primary, .btn-primary {
    background: var(--cub-red);
    border-color: var(--cub-red);
    color: #fff;
}

.btn.primary:hover, .action-btn.primary:hover, .btn-primary:hover {
    background: var(--cub-red-bright);
    border-color: var(--cub-red-bright);
}

/* ── feedback ────────────────────────────────── */
.toast {
    background: var(--cub-panel);
    border: 1px solid var(--cub-line);
    border-left: 2px solid var(--cub-red);
    border-radius: 2px;
    color: var(--cub-text);
    font-family: var(--cub-mono);
    font-size: .78rem;
}

.toast.success { border-left-color: var(--cub-green); }
.toast.error   { border-left-color: var(--cub-red-bright); }

.empty, .empty-state {
    color: var(--cub-muted);
    font-family: var(--cub-mono);
    font-size: .8rem;
}

/* ── code and API docs ───────────────────────── */
/* The developer pages are mostly syntax-highlighted samples; leaving these
   unstyled left bright IDE colours on a near-black page. */
.tok, .tokens, code, pre { font-family: var(--cub-mono); }
.kw   { color: #c678dd; }
.str  { color: #98c379; }
.fn   { color: #61afef; }
.arrow, .try { color: var(--cub-text-dim); }
.excol { color: var(--cub-muted); }

pre, .code-block {
    background: var(--cub-void);
    border: 1px solid var(--cub-line);
    border-radius: 2px;
}

/* ── second pass: the remaining cross-cutting pieces ──────────
   Chosen by how many templates use them, not by how they look in isolation.
   Deliberately NOT included: .ex / .excol (426 uses, but all on one page that
   already styles them well) and the calculator/dice classes. A page with a
   considered look of its own is better left alone than dragged into line. */

.card, .tool-card, .info-card {
    background: var(--cub-panel);
    border: 1px solid var(--cub-line);
    border-radius: 2px;
    transition: border-color .14s ease;
}

.card:hover, .tool-card:hover, .info-card:hover {
    border-color: var(--cub-line-warm);
}

.section-title, .cat-title {
    font-family: var(--cub-mono);
    letter-spacing: .1em;
    color: var(--cub-text);
}

.cat-lede, .section-lede, .dim, .jb-dim {
    color: var(--cub-muted);
    font-size: .82rem;
}

.slider-value {
    font-family: var(--cub-mono);
    color: var(--cub-red-bright);
    font-variant-numeric: tabular-nums;
}

/* CUB PROTECTOR's marketing page carries these on every feature it lists. */
.cp-module-tag {
    display: inline-block;
    padding: .12rem .45rem;
    border: 1px solid var(--cub-line);
    border-radius: 2px;
    background: var(--cub-red-ghost);
    font-family: var(--cub-mono);
    font-size: .62rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--cub-text-dim);
}

.cp-feature {
    background: var(--cub-panel);
    border: 1px solid var(--cub-line);
    border-radius: 2px;
}

/* The site's custom toggles, so they read as part of this theme rather than
   as the old palette's blue. */
.toggle-slider {
    background: var(--cub-line);
    border: 1px solid var(--cub-line);
    transition: background .16s ease, border-color .16s ease;
}

input:checked + .toggle-slider {
    background: var(--cub-red);
    border-color: var(--cub-red-bright);
}

/* ═══════════════════════════════════════════════════════════
   RETHEMING PAGES THROUGH THEIR OWN VARIABLES
   ═══════════════════════════════════════════════════════════
   25 pages theme themselves with --accent, 16 with --text, 12 with --border,
   11 with --bg, and so on. Redefining those here rethemes each page through
   the architecture it already has, instead of chasing its selectors one at a
   time — one block covers more ground than the two passes above combined.

   This works because the theme stylesheet is injected immediately before
   </head>, so it lands after every page's own <style>. Two templates put a
   <style> in the body and win over this by position: cubprotector_verify.html
   and mail.html. They need doing by hand.

   Only colour tokens are touched. Sizing and spacing variables are left alone
   — those carry each page's layout, and overriding them centrally would break
   pages in ways nobody would connect back to this file. */
:root, body {
    /* accents */
    --accent:        #a50f12;
    --accent-light:  #d10a10;
    --accent-lite:   #d10a10;
    --accent-glow:   rgba(209, 10, 16, .35);
    --red:           #d10a10;
    --primary:       #a50f12;

    /* grounds */
    --bg:            #08090b;
    --bg-panel:      #0e1013;
    --bg-card:       #0e1013;
    --panel:         #0e1013;
    --surface:       #0e1013;
    --surface2:      #15181d;

    /* lines */
    --border:        #1e2228;
    --line:          #1e2228;

    /* type */
    --text:          #dfe2e6;
    --text-muted:    #98a0aa;
    --muted:         #6b737e;
    --dim:           #6b737e;

    /* state — kept distinct from the accent so meaning survives */
    --success:       #3f9e63;
    --ok:            #3f9e63;
    --green:         #3f9e63;
    --warn:          #b98b2e;
    --danger:        #d10a10;
}

/* A white page ground is the one thing that cannot be allowed through: it is
   the difference between "themed" and "blinding". */
body { background: var(--cub-void, #08090b); color: var(--cub-text, #dfe2e6); }

/* ═══════════════════════════════════════════════════════════
   THE DESIGN LANGUAGE, NOT JUST THE PALETTE
   ═══════════════════════════════════════════════════════════
   Everything above recoloured the site. It did not make it look like the new
   homepage, which is why the pages still read as the old layout wearing a dark
   skin. This maps the site's real components onto the homepage's own furniture:

     square corners (2px, never pills)   mono uppercase micro-labels
     1px hairline borders                a header strip on every card
     one red accent, used sparingly      generous dark ground

   Structure is untouched — only how it is drawn. */

/* ── section headings become the homepage's rule-and-label ── */
.category-title, .section-title, h2.cat-title, .tools-section > h2 {
    display: flex;
    align-items: baseline;
    gap: .6rem;
    margin: 1.6rem 0 .75rem;
    padding-bottom: .4rem;
    border-bottom: 1px solid var(--cub-line);
    font-family: var(--cub-mono);
    font-size: .82rem !important;
    font-weight: 700;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--cub-text);
}

/* ── cards get a header strip and hard edges ── */
.feature-card, .tool-card, .card, .bot-card, .info-card, .plugin-card {
    position: relative;
    background: #0e1013;
    border: 1px solid var(--cub-line);
    border-radius: 2px;
    overflow: hidden;
    transition: border-color .16s ease, transform .16s ease;
}

.feature-card:hover, .tool-card:hover, .card:hover,
.bot-card:hover, .info-card:hover, .plugin-card:hover {
    border-color: var(--cub-red-dim);
    transform: none;                    /* the old lift is not this design */
}

/* The hairline at the top of each card is what reads as a "file". */
/* (the top-edge hairline is defined further down, scoped correctly) */

.feature-card h3, .tool-card h3, .bot-card h3, .card h3 {
    font-family: var(--cub-mono);
    font-size: .82rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--cub-text);
}

.feature-card p, .tool-card p, .bot-card p, .card p {
    font-size: .78rem;
    line-height: 1.6;
    color: var(--cub-text-dim);
}

/* ── badges and pills become tags ── */
.feature-badge, .badge, .tag, .pill, .chip {
    border-radius: 2px !important;
    font-family: var(--cub-mono);
    font-size: .6rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    background: var(--cub-red-ghost);
    border: 1px solid var(--cub-line);
    color: var(--cub-text-dim);
}

/* ── page heroes become panels ── */
.hero, .page-hero, .tools-hero {
    background: #0e1013;
    border: 1px solid var(--cub-line);
    border-radius: 2px;
}

.hero h1, .page-hero h1 {
    font-family: 'Oswald', var(--cub-mono);
    letter-spacing: .04em;
    text-transform: uppercase;
}

/* The eyebrow above a page title, in the homepage's voice. */
.hero .eyebrow, .page-hero .eyebrow, .hero-badge {
    font-family: var(--cub-mono);
    font-size: .62rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--cub-red-bright);
    background: none;
    border: 0;
}

/* ── kill the rounded, glossy leftovers of the old look ── */
.btn, .action-btn, .cta-button, .feature-card, .tool-card, .card,
.result-card, .settings-group, input, textarea, select,
.form-input, .form-select, .form-control {
    border-radius: 2px !important;
}

/* Nothing in this design glows or lifts on hover. */
.feature-card:hover, .tool-card:hover, .card:hover, .btn:hover {
    box-shadow: none !important;
}

/* ═══════════════════════════════════════════════════════════
   COOKIE CONSENT
   ═══════════════════════════════════════════════════════════
   Consent chrome sits on top of everything and was the last thing still
   wearing the old palette — rounded, blue, and unmistakably from a different
   site. It is also the first thing a new visitor sees, so it matching matters
   more than most of what is themed above.

   Layout and behaviour untouched: this is a legal control, and it must keep
   working exactly as it does. Only how it is drawn changes. */

.cookie-banner, .cookie-declined-bar {
    background: linear-gradient(180deg, rgba(14,16,20,.97), rgba(7,8,10,.99));
    border-top: 1px solid var(--cub-line);
    border-radius: 0;
    backdrop-filter: blur(8px);
    color: var(--cub-text-dim);
}

.cookie-banner-text, .cookie-declined-bar-text {
    font-size: .78rem;
    line-height: 1.6;
    color: var(--cub-text-dim);
}

.cookie-banner-text strong, .cookie-banner-text b { color: var(--cub-text); }
.cookie-banner-text a, .cookie-declined-bar-text a { color: var(--cub-red-bright); }

/* Buttons: square, mono, uppercase — the same language as the rest. Accept is
   the only one that carries the accent; a row of three red buttons would tell
   the reader nothing about which is the ordinary choice. */
.cookie-btn-accept, .cookie-btn-decline, .cookie-btn-manage,
.cookie-float-btn, .btn-dismiss-bar, .btn-manage-small, .btn-reaccept,
.cookie-modal-footer button {
    border-radius: 2px !important;
    font-family: var(--cub-mono);
    font-size: .66rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    padding: .5rem .9rem;
    border: 1px solid var(--cub-line);
    background: var(--cub-panel);
    color: var(--cub-text-dim);
    transition: border-color .14s ease, color .14s ease, background .14s ease;
    box-shadow: none !important;
}

.cookie-btn-accept {
    background: var(--cub-red);
    border-color: var(--cub-red);
    color: #fff;
}

.cookie-btn-accept:hover {
    background: var(--cub-red-bright);
    border-color: var(--cub-red-bright);
}

.cookie-btn-decline:hover, .cookie-btn-manage:hover,
.cookie-float-btn:hover, .btn-dismiss-bar:hover,
.btn-manage-small:hover, .btn-reaccept:hover {
    border-color: var(--cub-red-dim);
    background: var(--cub-raised);
    color: #fff;
}

/* The preferences modal. */
.cookie-modal-overlay {
    /* Same story as .cookie-banner: only shared.css took this out of the flow,
       so on a page that doesn't load it the overlay is a static block whose
       full height is added to the page's scroll. visibility:hidden below
       stops it being seen but still reserves that layout space — position:
       fixed is what actually removes it. */
    position: fixed;
    inset: 0;
    z-index: 9500;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s;
    background: rgba(0, 0, 0, .82);
}
.cookie-modal-overlay.visible { opacity: 1; pointer-events: auto; }

@media (prefers-reduced-motion: reduce) {
    .cookie-modal-overlay { transition: none; }
}

/* Only blur while the modal is actually open. shared.css toggles `.visible`. */
.cookie-modal-overlay.visible {
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

/* A zero-opacity overlay is still a live layer: it takes clicks and, with a
   backdrop-filter, still filters everything behind it — which is what blurred
   the whole site. Make the closed state genuinely inert, not just invisible. */
.cookie-modal-overlay:not(.visible) {
    visibility: hidden;
    pointer-events: none;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
}

.cookie-modal {
    background: linear-gradient(180deg, rgba(14,16,20,.98), rgba(7,8,10,.99));
    border: 1px solid var(--cub-line);
    border-radius: 2px !important;
    color: var(--cub-text);
}

.cookie-modal-header, .cookie-modal-footer {
    border-color: var(--cub-line);
}

.cookie-modal-header h2, .cookie-modal-header h3 {
    font-family: var(--cub-mono);
    font-size: .86rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--cub-text);
}

.cookie-modal-desc, .cookie-toggle-desc { color: var(--cub-muted); font-size: .76rem; }
.cookie-toggle-name { font-family: var(--cub-mono); font-size: .72rem; letter-spacing: .08em;
                      text-transform: uppercase; color: var(--cub-text); }

.cookie-toggle-row {
    background: var(--cub-panel);
    border: 1px solid var(--cub-line);
    border-radius: 2px;
}

.cookie-modal-close { color: var(--cub-muted); background: none; border: 0; }
.cookie-modal-close:hover { color: var(--cub-red-bright); }

/* The consent switches, in the theme's red rather than the old blue. */
.cookie-switch-slider { background: var(--cub-line); border-radius: 2px; }
.cookie-switch input:checked + .cookie-switch-slider { background: var(--cub-red); }

/* ═══════════════════════════════════════════════════════════
   THE FOOTER
   ═══════════════════════════════════════════════════════════
   Shared across every page, and still the old navy with light-grey links.
   Matched to the homepage's own footer: hairline top edge, mono micro-type,
   links that are quiet until hovered. */

.footer {
    background: #08090b;
    border-top: 1px solid var(--cub-line);
    color: var(--cub-muted);
}

.footer-inner { background: transparent; }

.footer-text, .footer-text * {
    font-family: var(--cub-mono);
    font-size: .66rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--cub-muted);
}

.footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: 1.1rem;
    justify-content: center;
}

.footer-links a {
    font-family: var(--cub-mono);
    font-size: .68rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--cub-text-dim);
    text-decoration: none;
    transition: color .14s ease;
}

.footer-links a:hover,
.footer-links a:focus-visible { color: var(--cub-red-bright); }

/* The status line under the links, in the theme's own voice. */
.footer .status-line, .footer small {
    font-family: var(--cub-mono);
    font-size: .6rem;
    letter-spacing: .16em;
    color: var(--cub-line-warm);
}

/* ═══════════════════════════════════════════════════════════
   CARD GRIDS AND CARD TYPE
   ═══════════════════════════════════════════════════════════
   .features-grid was a centred flex row of fixed 275px cards. Three cards in a
   1360px container therefore sat in the middle with dead space either side, and
   a section with one card left it floating alone in the centre of the page.

   A real grid instead: equal columns that fill the row, aligned with the
   section rule above them, stepping down at deliberate widths. */

.features-grid, .cards-grid {
    display: grid !important;
    grid-template-columns: repeat(auto-fit, minmax(240px, 300px)) !important;
    justify-content: center !important;
    gap: 1rem !important;
    max-width: 1360px !important;
    width: 100%;
    margin: 0 auto 1.6rem !important;
}

/* The flex sizing on the children fights the grid — clear it. */
.features-grid > *, .cards-grid > * {
    flex: initial !important;
    width: auto !important;
    min-width: 0;
}



/* Phones: one full-width card, rather than a 250px column floating centre. */
@media (max-width: 580px) {
    .features-grid, .cards-grid {
        grid-template-columns: minmax(0, 1fr) !important;
        justify-content: stretch !important;
    }
}

/* ── card type: readable, not miniature ─────────────────────
   The mono treatment made everything small. A label can be tiny; the thing
   people are actually reading cannot. */
.feature-card, .tool-card, .bot-card, .card {
    padding: 1.15rem 1.15rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: .55rem;
}

/* A card with a file number carries an absolutely-positioned "FILE // 001" at
   0.7rem and a hairline at 2rem. My padding only reserved 1.15rem, so the
   heading sat underneath the label. Reserve the space the label actually
   occupies rather than nudging the heading down and hoping. */
.feature-card[data-cub-file] {
    padding-top: 2.7rem;
}

/* My decorative top-edge hairline and the file label are both ::before on the
   same element — the label wins on specificity, so scope mine to cards that do
   not have one instead of leaving a rule that silently never applies. */
.feature-card:not([data-cub-file])::before,
.tool-card::before,
.bot-card::before {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: linear-gradient(90deg, var(--cub-red-dim), transparent 60%);
}

.feature-card h3, .tool-card h3, .bot-card h3, .card h3 {
    font-size: .95rem !important;
    letter-spacing: .1em;
    line-height: 1.3;
    margin: 0;
}

.feature-card p, .tool-card p, .bot-card p, .card p {
    font-size: .86rem !important;
    line-height: 1.65 !important;
    color: var(--cub-text-dim);
    margin: 0;
}

/* The badge sits at the bottom of the card rather than wherever the text ends,
   so a row of cards lines up along its bottom edge. */
.feature-card .feature-badge, .tool-card .feature-badge {
    align-self: flex-start;
    margin-top: auto;
    padding: .4rem .7rem;
}

/* Section rules span the same width as the grid beneath them. */
.category-title, .section-title { max-width: none; }

/* ═══════════════════════════════════════════════════════════
   READABILITY OF THE SECTOR LABELS
   ═══════════════════════════════════════════════════════════
   The sector tag was --cub-red-dim (#5c0508) on the #08090b ground: 1.40:1,
   which is not dim, it is invisible. --cub-red is no better at 2.54:1.

   #e0464a measures 4.88:1 — over the 4.5:1 needed for small text — and still
   reads as the same red. Contrast was the problem, not the hue.

   The label is also no longer 0.64rem: a wayfinding element people are meant
   to scan should not be the smallest type on the page. */
.cub-sector-tag,
.category-title[data-cub-sector]:not([data-cub-anchored])::before {
    color: #e0464a !important;
    font-size: .72rem;
    letter-spacing: .16em;
}

.category-title, .section-title {
    font-size: .95rem !important;
    color: var(--cub-text) !important;
}

/* The hairline rules either side of a centred heading were the same near-black
   as the ground. One step up, so the rule reads as a rule. */
.category-title, .section-title { border-bottom-color: var(--cub-line-warm); }

/* Same problem anywhere else red-dim was used for text rather than a border. */
.stat-label, .meta-label, .kv dt, .lbl {
    color: var(--cub-text-dim);
}

/* ── the sector label sits above its heading ─────────────────
   Inline, "SECTOR 06 //" competed with the heading for the same line and read
   as part of the title. Stacked, it works the way a section number should: a
   small marker, then the thing it is numbering. */
.category-title, .section-title {
    flex-direction: column;
    align-items: center;
    gap: .3rem;
    text-align: center;
}

.cub-sector-tag {
    display: block;
    line-height: 1;
}

/* The rules either side were sized for a single line of text; centred over two
   lines they need to sit against the block rather than the first line. */
.category-title { align-items: center; }

/* ═══════════════════════════════════════════════════════════
   PAGE HEADERS  (.section-eyebrow / .section-title / .section-description)
   ═══════════════════════════════════════════════════════════
   The eyebrow was tiny and the title sat hard left with its rule running off
   to the right, so the block read as three unrelated things stacked up rather
   than one page header.

   Centred, and the eyebrow given real size — it is the first line on the page
   and was smaller than the body copy underneath it. */

.about-section .container > .section-eyebrow,
.section-eyebrow {
    display: block;
    text-align: center;
    margin: 2.4rem auto .55rem;
    font-family: var(--cub-mono);
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: .34em;
    text-transform: uppercase;
    color: #e0464a;
}

.about-section .container > .section-title,
h2.section-title {
    display: block !important;
    text-align: center !important;
    margin: 0 auto 1rem !important;
    padding: 0 0 .8rem !important;
    border-bottom: 1px solid var(--cub-line-warm);
    max-width: 1360px;
    font-family: 'Oswald', var(--cub-mono);
    font-size: 2.1rem !important;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--cub-text) !important;
}

.section-description {
    max-width: 62ch;
    margin: 0 auto 2.2rem;
    text-align: center;
    font-size: .94rem;
    line-height: 1.7;
    color: var(--cub-text-dim);
}

@media (max-width: 700px) {
    .section-eyebrow { font-size: .82rem; letter-spacing: .26em; }
    h2.section-title { font-size: 1.5rem !important; }
}


/* ── every card grid, not just .features-grid ────────────────
   Same rule for the other grid class names in use across the site, so
   "four across and centred" is true everywhere rather than on one page. */
.tools-grid, .bots-grid, .grid-cards, .card-grid, .apps-grid, .plugin-grid {
    display: grid !important;
    grid-template-columns: repeat(auto-fit, minmax(240px, 300px)) !important;
    justify-content: center !important;
    gap: 1rem !important;
    max-width: 1360px !important;
    margin: 0 auto 1.6rem !important;
}

.tools-grid > *, .bots-grid > *, .grid-cards > *,
.card-grid > *, .apps-grid > *, .plugin-grid > * {
    flex: initial !important;
    width: auto !important;
    min-width: 0;
}

@media (max-width: 580px) {
    .tools-grid, .bots-grid, .grid-cards, .card-grid, .apps-grid, .plugin-grid {
        grid-template-columns: minmax(0, 1fr) !important;
        justify-content: stretch !important;
    }
}

/* ═══════════════════════════════════════════════════════════
   DASHBOARD SURFACES
   ═══════════════════════════════════════════════════════════
   The dashboards are full-screen apps: `.admin-sidebar` is position:fixed at
   top:0 with z-index:1001, so the shared navbar was rendering *underneath* it
   — the CUBSOFTWARE brand sat behind the sidebar and the bar looked broken.

   The fix is layout, not hiding things:
     * the shared bar goes above the sidebar in the stack
     * the sidebar starts below the bar instead of at the top of the viewport
     * the content column is pushed down by the same amount

   The dashboard's own `.mobile-header` goes, because that is a second header
   doing the same job. The sidebar stays: it is the app's navigation. */

:root { --cx-bar-h: 3.4rem; }

.mobile-header { display: none !important; }

/* Above the sidebar's 1001, so the bar is never covered. */
.cx-top { z-index: 1100 !important; }

.admin-sidebar {
    top: var(--cx-bar-h) !important;
    bottom: 0 !important;
    height: auto !important;
    z-index: 1001;
}

/* The content column sits beside the sidebar and below the bar. */
.admin-main, .dashboard-main, .admin-content {
    padding-top: .5rem;
}

/* A marketing footer under an app is fine, but quieter — it should not compete
   with the thing people came to use. */
body:has(.admin-sidebar) .footer {
    margin-top: 0;
    padding: 1rem 0;
    font-size: .72rem;
}

@media (max-width: 900px) {
    /* On mobile the sidebar slides in over the content, so it needs the full
       height below the bar rather than a fixed offset. */
    .admin-sidebar { top: var(--cx-bar-h) !important; }
}

/* ═══════════════════════════════════════════════════════════
   THE HOMEPAGE'S VARIABLE NAMES
   ═══════════════════════════════════════════════════════════
   The shared header CSS was lifted straight out of the homepage, so it refers
   to that page's variable names — --tx-d, --mut, --red-b, --red-d, --line-r,
   --mono. The homepage declares them in its own <style>; no other page did.

   That is why the header looked right on the homepage and wrong everywhere
   else: identical CSS, but on other pages its colours resolved to nothing —
   transparent borders, inherited text colour, no mono face.

   Declared here with the homepage's own values, so one definition of the
   header genuinely produces one appearance. */
:root, body {
    --void:    #060709;
    --panel:   #0a0c0e;
    --panel2:  #0d0f12;
    --line:    #15181d;
    --line-r:  #2b1418;
    --red:     #a50f12;
    --red-b:   #d10a10;
    --red-d:   #5c0508;
    --red-g:   rgba(165, 15, 18, .09);
    --tx:      #f2f5f7;
    --tx-d:    #c2c9d0;
    --mut:     #8b939c;
    --dim:     #5a626b;
    --grn:     #3f9e63;
    --amb:     #b98b2e;
    --mono:    'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ── call-to-action buttons ──────────────────────────────────
   .btn-discord and friends were 12px pills in Inter with no letter-spacing —
   the old look, and the most prominent control on the affiliate page. Brought
   into the same language as every other button here: square, mono, uppercase,
   and it does not lift on hover because nothing in this design does. */
.btn-discord, .btn-dashboard, .cta-button, .btn-apply, .apply-btn {
    border-radius: 2px !important;
    font-family: var(--cub-mono) !important;
    font-size: .74rem !important;
    letter-spacing: .14em !important;
    text-transform: uppercase !important;
    font-weight: 600;
    padding: .85rem 1.6rem !important;
    background: var(--cub-red) !important;
    border: 1px solid var(--cub-red) !important;
    color: #fff !important;
    transition: background .14s ease, border-color .14s ease;
}

.btn-discord:hover, .btn-dashboard:hover, .cta-button:hover,
.btn-apply:hover, .apply-btn:hover {
    background: var(--cub-red-bright) !important;
    border-color: var(--cub-red-bright) !important;
    transform: none !important;
    box-shadow: none !important;
}

/* ── Cookie banner: structure ────────────────────────────────────────
   The colours for this live further up, but the rules that take the
   banner out of the flow (position/transform) were only ever in
   shared.css. Pages that don't load shared.css — home.html is all
   inline <style> — rendered the banner as a static block sitting
   between the last section and the footer, and added its height to the
   page's scroll. cub-theme.css is injected on every themed host, so
   putting the structure here makes the banner behave everywhere. */
.cookie-banner {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 9000;
    padding: 1.25rem 2rem;
    box-shadow: 0 -4px 32px rgba(0, 0, 0, 0.5);
    transform: translateY(100%);
    transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.cookie-banner.visible { transform: translateY(0); }
.cookie-banner-inner {
    max-width: 1200px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
}
.cookie-banner-text { flex: 1; min-width: 200px; }
.cookie-banner-text p { margin: 0 0 0.25rem 0; line-height: 1.6; }
.cookie-banner-text p:last-child { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
    .cookie-banner { transition: none; }
}

/* ── Remaining floating chrome: structure ────────────────────────────
   The last of the elements shared.css takes out of the flow. On pages
   that don't load shared.css these rendered as static blocks stacked
   after the last section, each adding its height to the page scroll.
   Positioning only — colours stay wherever they already are. */
.cookie-declined-bar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 8900;
    padding: 0.6rem 2rem;
    transform: translateY(100%);
    transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.cookie-declined-bar.visible { transform: translateY(0); }

.cookie-float-btn {
    position: fixed;
    bottom: 1.25rem;
    right: 1.25rem;
    z-index: 8800;
    width: 42px;
    height: 42px;
    border-radius: 50%;
    cursor: pointer;
    /* shown by the consent script once a choice has been stored, via
       .visible — that rule lives in shared.css, so it has to be repeated
       here or the button stays hidden on pages that don't load it. */
    display: none;
    align-items: center;
    justify-content: center;
}
.cookie-float-btn.visible { display: flex; }

.bmac-float {
    position: fixed;
    bottom: 1.5rem;
    right: 1.5rem;
    z-index: 500;
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 10px 16px;
    border-radius: 999px;
    text-decoration: none;
}

@media (prefers-reduced-motion: reduce) {
    .cookie-declined-bar { transition: none; }
}

/* ── Shared header: small screens ────────────────────────────────────
   This file is injected after home.html's inline <style>, and both
   declare .cx-nav at the same specificity — so the theme's display:flex
   was overriding home.html's @media{.cx-nav{display:none}} and the nav
   never collapsed. Media queries carry no specificity, so the responsive
   rules have to live here, in the file that wins.

   Hiding the nav outright (the old behaviour) left phones with no
   navigation at all; it becomes a dropdown instead. */
.cx-burger {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    width: 42px;
    height: 42px;              /* a real 42px tap target */
    padding: 0 9px;
    background: none;
    border: 1px solid var(--cub-line, rgba(165,15,18,.3));
    border-radius: 2px;
    cursor: pointer;
}
.cx-burger span {
    display: block;
    height: 2px;
    width: 100%;
    background: var(--cub-red-bright, #d10a10);
    transition: transform .18s ease, opacity .18s ease;
}
.cx-top.nav-open .cx-burger span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.cx-top.nav-open .cx-burger span:nth-child(2) { opacity: 0; }
.cx-top.nav-open .cx-burger span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 860px) {
    .cx-top {
        grid-template-columns: 1fr auto auto;
        gap: .6rem;
        padding: .55rem .75rem;
    }
    .cx-burger { display: flex; }

    /* .cx-top is position:sticky, so it is the containing block for this. */
    .cx-nav {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        padding: .35rem 0 .6rem;
        background: rgba(6, 7, 9, .985);
        border-bottom: 1px solid var(--cub-line, rgba(165,15,18,.3));
        box-shadow: 0 14px 30px rgba(0, 0, 0, .75);
        max-height: calc(100vh - 100%);
        overflow-y: auto;
        overscroll-behavior: contain;
    }
    .cx-top.nav-open .cx-nav { display: flex; }
    .cx-nav a {
        padding: .85rem 1.1rem;          /* 44px+ tap targets */
        border-bottom: 1px solid rgba(255, 255, 255, .05);
        border-left: 2px solid transparent;
    }
    .cx-nav a.on { border-bottom-color: rgba(255,255,255,.05); border-left-color: var(--cub-red-bright, #d10a10); }
    /* The account block is part of the menu on mobile, not hidden. */
    .cx-acct { display: block; padding: .5rem 1.1rem 0; }

    .cx-brand b { font-size: 1rem; min-width: 0; }
    .cx-brand i { font-size: .55rem; letter-spacing: .14em; }
    .cx-brand img { width: 30px; height: 30px; }
    .cx-tbtn { font-size: .6rem; padding: .5rem .6rem; letter-spacing: .08em; }
}

/* Narrow phones: the terminal button is the first thing to go — the
   terminal is still reachable by scrolling to it. */
@media (max-width: 560px) {
    .cx-tbtn { display: none; }
    .cx-top { grid-template-columns: 1fr auto; }
}

@media (prefers-reduced-motion: reduce) {
    .cx-burger span { transition: none; }
}

/* ── Touch: readable labels and reachable controls ───────────────────
   The theme's mono labels sit around 11.2px, which holds up on a phone
   and is deliberate. These are the ones that don't: badges at 9.6px and
   consent controls at 10.6px, plus anything a finger has to hit.

   Scoped to coarse pointers so the desktop design is untouched — a
   width query alone would also catch a small laptop window. */
@media (pointer: coarse) and (max-width: 860px) {
    .feature-badge { font-size: .72rem; padding: .3rem .55rem; }

    /* Consent controls: real buttons a person has to tap, on every page. */
    .cookie-btn-accept,
    .cookie-btn-decline,
    .cookie-btn-manage,
    .cookie-modal-footer button {
        font-size: .78rem;
        min-height: 44px;
        padding: .6rem 1rem;
    }
    .cookie-banner { padding: 1rem .9rem; }
    .cookie-banner-inner { gap: .75rem; }
    /* The three buttons stop being a squeezed row and become a stack. */
    .cookie-banner-actions { display: flex; flex-wrap: wrap; gap: .5rem; width: 100%; }
    .cookie-banner-actions > * { flex: 1 1 auto; }

    .cookie-float-btn { width: 46px; height: 46px; }

    /* Form controls are the other thing that must be tappable: a 28px
       input is a miss as often as a hit, and iOS zooms the page when a
       focused field is under 16px. */
    input:not([type=checkbox]):not([type=radio]):not([type=range]),
    select,
    textarea {
        font-size: 16px;
        min-height: 44px;
    }
    textarea { min-height: 88px; }
}

/* ── The header stays on screen (17 Sep 2026) ────────────────────────────
   .cx-top is position:sticky, but shared.css gives <body> overflow-x:hidden.
   That makes <body> a scroll box of its own (one that never scrolls), and
   sticky pins to the nearest scroll box — so on every page but the homepage
   the header scrolled away, and on a phone the menu button was out of reach
   until you scrolled all the way back up. `clip` cuts off sideways overflow
   the same way without making a scroll box. A page that locks both axes
   (overflow: hidden) is unaffected: clip next to hidden computes to hidden. */
html > body { overflow-x: hidden; overflow-x: clip; }

/* ═══════════════════════════════════════════════════════════
   ACCOUNT MENU + MOBILE NAV (16 Sep 2026)
   ═══════════════════════════════════════════════════════════
   Desktop: the account menu was one 568px column that ran off the bottom of
   the screen — Admin Dashboard was cut in half and Sign Out out of reach. It
   is now grouped in two columns under a header naming who is signed in.

   Mobile: the menu panel rendered as a 136px box in the middle of the screen
   (the desktop `justify-self:center` shrink-wraps an absolutely positioned
   grid item), and the account dropdown opened off the left edge inside it,
   where the panel's own scroll box clipped it — so on a phone there was no way
   to reach any account link at all. The panel now spans the width, dims and
   locks the page behind it, and the account links are laid out inside it
   (17 Sep 2026: shown outright rather than behind a collapsed row). */
.cx-acct .acct-avatar {
    width: 22px; height: 22px; border-radius: 50%;
    object-fit: cover; flex: 0 0 auto;
    border: 1px solid var(--line, rgba(255,255,255,.12));
}
.cx-acct .acct-avatar-letter {
    display: inline-grid; place-items: center;
    background: var(--panel2, #15181d); color: #fff;
    font-size: .68rem; letter-spacing: 0;
}
.cx-acct .dropdown-menu.acct-menu {
    width: min(32rem, calc(100vw - 2rem));
    min-width: 0;
    padding: 0;
    background: #08090c;
    max-height: calc(100vh - 5.5rem);
    overflow-y: auto;
    overscroll-behavior: contain;
}
.cx-acct .nav-dropdown::after { width: min(32rem, calc(100vw - 2rem)); }
.acct-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; padding: .85rem 1rem;
    border-bottom: 1px solid var(--line, rgba(255,255,255,.1));
}
.acct-who { min-width: 0; }
.acct-who strong {
    display: block; color: #fff; font-size: .9rem; letter-spacing: .03em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.acct-provider {
    display: block; margin-top: .15rem;
    font-size: .6rem; letter-spacing: .1em; text-transform: uppercase;
    color: var(--mut, #7c848d);
}
.cx-acct .dropdown-menu .acct-head a.acct-primary {
    flex: 0 0 auto; display: inline-block;
    padding: .45rem .8rem; border: 1px solid var(--line-r, rgba(165,15,18,.45));
    border-left-width: 1px; color: #fff;
    font-size: .66rem; letter-spacing: .1em; text-transform: uppercase;
}
.cx-acct .dropdown-menu .acct-head a.acct-primary:hover {
    background: rgba(165, 15, 18, .2); border-left-color: var(--line-r, rgba(165,15,18,.45));
}
/* Columns, not a grid: groups of different lengths flow down each column
   instead of leaving a gap beside the shorter one. */
.cx-acct .acct-grid {
    display: block; columns: 2; column-gap: 0;
    padding: .45rem 0;
}
.cx-acct .acct-group { padding: .2rem 0; min-width: 0; break-inside: avoid; }
.acct-foot {
    border-top: 1px solid var(--line, rgba(255,255,255,.1));
    padding: .3rem 0;
}

@media (max-width: 860px) {
    /* Full width, not a shrink-wrapped box in the middle of the screen. */
    .cx-nav {
        justify-self: stretch;
        max-height: calc(100vh - 100%);
        max-height: calc(100dvh - 100%);
        padding-bottom: calc(.6rem + env(safe-area-inset-bottom, 0px));
    }
    /* The page behind the open menu dims and stops scrolling. The lock goes on
       <html>, which is what scrolls: overflow on <body> would make it a scroll
       box and unpin the sticky header (see "The header stays on screen"). */
    html.cx-nav-open { overflow: hidden; }
    body.cx-nav-open::after {
        content: ''; position: fixed; inset: 0; z-index: 9150;
        background: rgba(0, 0, 0, .6);
    }
    /* The cookie banner and the "cookies declined" bar sit at z-index 9000 and
       covered the bottom of the open menu, Sign Out included. While the menu is
       open it goes above them; they come back when it closes. */
    body.cx-nav-open .cx-top { z-index: 9200 !important; }

    /* Site links: a 2x2 grid instead of four full-width rows, so the account
       links below start on the first screen. */
    .cx-top.nav-open .cx-nav {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-content: start;
    }
    .cx-nav > a { display: flex; align-items: center; min-height: 52px; min-width: 0; padding: .7rem 1.1rem; }
    .cx-nav > a:nth-of-type(odd) { border-right: 1px solid rgba(255, 255, 255, .05); }

    .cx-acct {
        grid-column: 1 / -1;
        display: block; padding: 0;
        border-top: 1px solid rgba(255, 255, 255, .06);
    }
    .cx-acct .nav-auth { display: block; }
    .cx-acct .nav-dropdown { position: static; }
    .cx-acct .nav-dropdown::after { display: none !important; }
    .cx-acct .nav-login-btn {
        display: flex; justify-content: center;
        margin: .75rem 1.1rem .35rem; padding: .8rem;
        min-height: 48px; border: 1px solid var(--line-r, rgba(165,15,18,.45));
        color: #fff;
    }

    /* Account links are shown, not folded behind an "Account" row. Inside a
       menu that was a third tap, and once open its list ran past the bottom
       of the screen. The "Account" button is not needed in here at all. */
    .cx-acct .nav-link.dropdown-toggle { display: none; }
    .cx-acct .dropdown-menu.acct-menu,
    .cx-acct .nav-dropdown:hover .dropdown-menu.acct-menu {
        position: static; display: block;
        opacity: 1; visibility: visible; transform: none; transition: none;
        width: auto; min-width: 0; max-height: none; overflow: visible;
        margin: 0; padding: 0 0 .2rem; border: 0; border-radius: 0;
        box-shadow: none; background: none;
    }

    .cx-acct .acct-head { padding: .75rem 1.1rem; gap: .75rem; border-bottom: 0; }
    .cx-nav .cx-acct .acct-head a.acct-primary {
        display: inline-flex; align-items: center; min-height: 44px;
        padding: 0 .9rem; font-size: .7rem;
    }
    /* Each group's links as two columns of tiles: big enough to hit, and half
       the height of one link per row. */
    .cx-acct .acct-grid { columns: auto; padding: 0; }
    .cx-acct .acct-group {
        display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: .4rem; padding: .3rem 1.1rem .55rem;
    }
    .cx-nav .cx-acct .dropdown-section-label {
        grid-column: 1 / -1; padding: .15rem 0 0; font-size: .62rem;
    }
    .cx-nav .cx-acct .acct-group a {
        display: flex; align-items: center; min-width: 0; min-height: 44px;
        padding: .45rem .7rem; overflow-wrap: anywhere;
        font-size: .78rem; letter-spacing: .03em; line-height: 1.25; color: #d5dade;
        background: rgba(255, 255, 255, .03);
        border: 1px solid rgba(255, 255, 255, .08);
        border-left: 2px solid var(--line-r, rgba(165,15,18,.45));
        border-radius: 2px;
    }
    .cx-nav .cx-acct .acct-group a:active { background: rgba(165, 15, 18, .22); color: #fff; }
    .cx-acct .acct-foot { padding: .5rem 1.1rem 0; border-top: 1px solid rgba(255, 255, 255, .06); }
    .cx-nav .cx-acct .acct-foot a {
        display: flex; align-items: center; justify-content: center; min-height: 46px;
        border: 1px solid rgba(209, 10, 16, .35); border-radius: 2px;
        font-size: .76rem; letter-spacing: .1em; text-transform: uppercase;
    }
}

/* Touch screens wider than the menu breakpoint (a phone on its side, a
   tablet) get the desktop account dropdown. A tap leaves the browser's hover
   state on the button, and hover opened the menu, so tapping "Account" again
   could never close it. There the button's tap alone opens and closes it. */
@media (hover: none) and (min-width: 861px) {
    .cx-acct .nav-dropdown:not(.open):hover::after { display: none; }
    .cx-acct .nav-dropdown:not(.open):hover .dropdown-menu,
    .cx-acct .nav-dropdown:not(.open) .dropdown-menu:hover {
        display: none; opacity: 0; visibility: hidden;
    }
}

/* ── Phones: consent banner, declined bar, footer (16 Sep 2026) ────────
   The first-visit banner covered about a third of a phone screen — two
   paragraphs at full size above three stacked buttons. Same words, smaller,
   with the buttons in one row. Footer links were 17px tall, and the declined
   bar's buttons 27–31px; both now meet a 44px touch target. */
@media (max-width: 600px) {
    .cookie-banner {
        padding: .7rem .85rem calc(.7rem + env(safe-area-inset-bottom, 0px));
    }
    .cookie-banner-inner { gap: .55rem; }
    .cookie-banner-text { min-width: 0; }
    .cookie-banner-text p { font-size: .76rem; line-height: 1.45; }
    .cookie-banner-actions {
        display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: .4rem; width: 100%;
    }
    .cookie-banner-actions > * {
        min-height: 44px; padding: .35rem .3rem;
        font-size: .7rem; letter-spacing: .03em; line-height: 1.2;
        white-space: normal;
    }
    .cookie-declined-bar {
        padding: .45rem .85rem calc(.45rem + env(safe-area-inset-bottom, 0px));
    }
    .cookie-declined-bar-actions button { min-height: 44px; }
    .cookie-declined-bar .btn-dismiss-bar { min-width: 44px; }
    .cookie-modal-close { min-width: 40px; min-height: 40px; }
    .footer-links {
        display: flex; flex-direction: row; flex-wrap: wrap;
        justify-content: center; gap: 0 .25rem;
    }
    .footer-links a {
        display: inline-flex; align-items: center; justify-content: center;
        min-height: 44px; padding: 0 .65rem;
    }
    /* This rule is more specific than shared.css's hide, so repeat it. */
    .footer-links a.footer-mobile-hide { display: none; }
}

/* On a phone the floating cookie button sat on top of real controls — the
   first textarea, a Calculate button, Notepad's sidebar toggle — on 21 pages.
   Cookie settings stay one tap away through the footer's "Cookies" link. The
   homepage hides that footer, so it keeps the button. */
@media (max-width: 768px) {
    body:not([data-cub-native]) .cookie-float-btn.visible { display: none; }
}

/* The dashboards' section menu button, added to this header on phones (see
   includes/cx-header.html). Shown where admin-dashboard.css moves the sidebar
   off-screen. */
.cx-sections { display: none; }
@media (max-width: 768px) {
    .cx-top.has-sections { grid-template-columns: 1fr auto auto; }
    .cx-sections {
        display: inline-flex; align-items: center; gap: .4rem;
        height: 42px; padding: 0 .75rem;
        background: none; color: #fff; cursor: pointer;
        border: 1px solid var(--cub-line, rgba(165,15,18,.3)); border-radius: 2px;
        font-family: var(--mono, inherit); font-size: .7rem; font-weight: 700;
        letter-spacing: .1em; text-transform: uppercase;
    }
    .cx-sections svg { color: var(--cub-red-bright, #d10a10); }
}
@media (max-width: 360px) {
    .cx-sections span { display: none; }
}

/* Pages whose body is a column flexbox and whose wrapper centres itself with
   margin: 0 auto: auto margins switch off stretching, so the wrapper grew to
   its widest unbreakable content (a table, a nowrap title) and ran off a
   phone. Cap the wrapper at the screen instead. The :not(#…) lifts this above
   the pages' own .wrap { max-width: 900px } rules. */
@media (max-width: 768px) {
    body > :is(div, main, section, article, form):not(#cub-never) {
        max-width: 100%;
        min-width: 0;
    }
}
