        /* Mobile-first card grid — the storefront's glass-card language */
        .card-grid {
            display: flex;
            flex-direction: column;
            gap: 1.2rem;
            margin-bottom: 2rem;
        }

        .card {
            background: var(--glass-bg);
            border-radius: 16px;
            padding: 1.5rem;
            position: relative;
            overflow: hidden;
            transition: border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
            cursor: pointer;
            backdrop-filter: blur(var(--glass-blur));
            -webkit-backdrop-filter: blur(var(--glass-blur));
            border: 1px solid var(--glass-border);
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 var(--glass-shine);
            width: 100%;
            box-sizing: border-box;
        }

        @media (hover: hover) and (pointer: fine) {
            .card:hover {
                transform: translateY(-4px);
                border-color: var(--glass-border-active);
                box-shadow: var(--glow-gold), 0 8px 32px rgba(0, 0, 0, 0.4);
            }
        }

        .card:active {
            transform: translateY(-2px);
            border-color: var(--glass-border-active);
        }

        /* ★ THE HEAD ROW (operator: "the right side of every card is empty" — screenshot of the
           LinkedIn card, title alone on the left with nothing filling the rest of the width).
           Every `.card-icon` used to sit ABOVE `.card-title` in normal block flow — fine for a
           card that already has a description filling the space below, empty-looking for one
           that does not (Network Nodes had no icon at all; Support Infrastructure's icon sat
           alone above an otherwise-empty band). Putting title and icon in one flex row uses the
           card's own width instead of leaving it beside the text. */
        .card-head {
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 1rem;
            margin-bottom: 0.8rem;
        }
        /* the row now owns the spacing both used to bring on their own — restated, not just
           overridden, so a card.css reader is not left wondering where the old margin went. */
        .card-head .card-title { margin-bottom: 0; }
        .card-head .card-icon {
            margin-bottom: 0;
            flex-shrink: 0;
        }
        /* 2rem was sized to match an emoji sitting ABOVE the title on its own line — beside a
           1.1rem title in the same row it reads oversized. 1.6rem balances the two without a
           second edit to the base .card-icon rule, which other surfaces may still depend on. */
        .card-head .card-icon svg { width: 1.6rem; height: 1.6rem; }

        .card-icon {
            font-size: 2rem;
            margin-bottom: 0.8rem;
            filter: drop-shadow(0 0 10px rgba(230, 57, 70, 0.55));
            animation: iconFloat 4s ease-in-out infinite;
            /* the colour an SVG icon's `stroke="currentColor"` picks up — untouched by the
               emoji icons still in use elsewhere (Shop, Support Infrastructure): a text glyph
               carries its own colour and does not read `color` at all. */
            color: var(--accent-red);
        }

        /* ★ SVG ICONS, ADDED ALONGSIDE THE EMOJI ONES (operator: "replacing their emojis with
           svg icons"). `font-size: 2rem` above sizes a text glyph; it does nothing to an <svg>,
           which is why this is a second, additive rule rather than an edit to the first — every
           `.card-icon` still carrying an emoji (Shop, Support Infrastructure) is completely
           unaffected. Sized to LAND ON the emoji's own visual footprint (2rem = 32px) rather
           than the SVG's own natural box, so an icon swapped from emoji to SVG or back does not
           reflow its card. Stroke, not fill, matching the nav's own icon language
           (app/shared/css/nav.css `.holo-btn svg`) — one line style for every icon in the app. */
        .card-icon svg {
            width: 2rem;
            height: 2rem;
            display: block;
            stroke: currentColor;
            fill: none;
            stroke-width: 1.5;
            stroke-linecap: round;
            stroke-linejoin: round;
        }
        /* ★ img SITS ALONGSIDE svg, NOT IN PLACE OF IT (operator 2026-08-15: internal.svg's
           sprite symbols extracted to standalone /app/internal.svg/*.svg files, referenced by
           <img> now instead of <use>). The selector above targets an inline <svg>; it has no
           reason to also match an <img>, and an <img> has no stroke/fill of its own to set — its
           color is BAKED into the file itself now (see app/internal.svg/README.md for why an
           <img>-loaded SVG can't inherit currentColor from this page at all). Only sizing needs
           restating here, so the card-icon box measures the same regardless of which element type
           is filling it. */
        .card-icon img {
            width: 2rem;
            height: 2rem;
            display: block;
        }

        @keyframes iconFloat {
            0%, 100% { transform: translateY(0) rotate(0deg); }
            50% { transform: translateY(-5px) rotate(3deg); }
        }

        .card-title {
            font-family: 'Orbitron', monospace;
            font-size: 1.1rem;
            font-weight: 600;
            background: linear-gradient(90deg, var(--accent-gold), var(--accent-silver));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            margin-bottom: 0.8rem;
            text-transform: uppercase;
            letter-spacing: 0.06em;
        }

        .card-description {
            color: var(--text-secondary);
            font-size: 0.92rem;
            line-height: 1.55;
            margin-bottom: 1.2rem;
        }
