/* Mashjoy Editorial Prototype Core CSS */
/* Inspiration: Newsreader.codesupply.co, Forbes, The Verge */

/* --- Font Declarations --- */

@font-face {
    font-family: 'Technor';
    src: url('/assets/fonts/Technor-Variable.woff2') format('woff2'),
         url('/assets/fonts/Technor-Variable.woff2') format('woff2-variations'),
         url('/assets/fonts/Technor-Variable.ttf') format('truetype');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Technor';
    src: url('/assets/fonts/Technor-Regular.woff2') format('woff2'),
         url('/assets/fonts/Technor-Regular.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Technor';
    src: url('/assets/fonts/Technor-Medium.woff2') format('woff2'),
         url('/assets/fonts/Technor-Medium.ttf') format('truetype');
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Technor';
    src: url('/assets/fonts/Technor-Semibold.woff2') format('woff2'),
         url('/assets/fonts/Technor-Semibold.ttf') format('truetype');
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Technor';
    src: url('/assets/fonts/Technor-Bold.woff2') format('woff2'),
         url('/assets/fonts/Technor-Bold.ttf') format('truetype');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Technor';
    src: url('/assets/fonts/Technor-Black.woff2') format('woff2'),
         url('/assets/fonts/Technor-Black.ttf') format('truetype');
    font-weight: 900;
    font-style: normal;
    font-display: swap;
}

/* @font-face for AristotelicaProText-Regular.woff2 removed at build time: the file is not in the repository. The stack falls back rather than waiting on a 404. */

/* @font-face for AristotelicaProText-Bold.woff2 removed at build time: the file is not in the repository. The stack falls back rather than waiting on a 404. */

/* @font-face for AristotelicaProText-Thin.woff2 removed at build time: the file is not in the repository. The stack falls back rather than waiting on a 404. */

/* --- Brand OS v2 Tokens (Editorial Refinement) --- */
:root {
    /* Color Architecture */
    --mj-red: #EC2227;
    --mj-black: #0A0A0A;
    --mj-navy: #1A2332;
    --mj-white: #FFFFFF;
    --mj-surface: #FDFDFD;
    /* THE PAGE CANVAS. Was #F7F7F7 — a pure neutral grey, and pure neutral greys
       are what a machine picks when nobody chose. It read dull under a red brand
       and flattened the white/black contrast the identity is built on.

       #FAF9F6 is a WARM paper white: 250/249/246, so red > green > blue by a
       whisper. Two things follow, and both are the point:
         · it LIFTS — four points closer to white, so the page stops feeling grey
         · it TONES — the SmartCMS surfaces at #FDFDFD now read as a cooler,
           cleaner white sitting ON something, rather than as a slightly brighter
           patch of the same grey. The section boundary is now a difference of
           TEMPERATURE, not just of lightness, which is what makes it feel chosen.

       The step is deliberately small. A canvas that competes with the elements
       standing on it is the opposite failure, and #FDFDFD has to keep reading as
       the white of the desk. */
    --mj-surface-alt: #FAF9F6;
    --mj-charcoal: #222222; /* Darker for better reading contrast */
    --mj-gray-200: #E5E5E5; /* Sharper ruling lines */
    --mj-gray-300: #CCCCCC;
    --mj-gray-400: #777777;

    /* Extended palette — mirrors design/elementor/kit-site-settings.json (SOURCE OF TRUTH). Added 2026-07-24 (P6). */
    --mj-ink: #171718;            /* body text (kit: Text — Body Ink) */
    --mj-text-muted: #404145;     /* muted meta text (kit: Text Muted) */
    --mj-red-hover: #BA1F33;
    --mj-red-subtle: #FFADAD;
    --mj-trust: #2563EB;          /* Intelligence / source links (kit: Accent — Trust Blue) */
    --mj-trust-subtle: #F5FBFF;
    --mj-growth: #10B981;         /* Trust / proof / best-pick (kit: Growth Green) */
    --mj-growth-subtle: #E3FAF2;
    /* GREEN AS TEXT NEEDS A DIFFERENT GREEN.
       `--mj-growth` is a FILL colour and a good one — badges, bars, the verified
       tick read cleanly with white on top. Set as TYPE on a light ground it
       measures 2.49:1, against 5.08 for the trust blue and 4.28 for the red. It
       is the only text colour on this site under 3:1, and Lucky read it exactly:
       "the contrast is a little bit too low and a little bit hard to read."

       Same hue (160°), darker value: 5.27:1 on --mj-surface, 4.91:1 on the green
       tint it often sits inside, 5.09:1 on paper — AA everywhere, and it lands
       between blue and red so the three link colours carry equal weight.

       FILLS ARE UNTOUCHED. Lucky: "you must still keep this green as it is for
       other applications." Only `color:` moved. */
    --mj-growth-text: #057A53;
    --mj-alert: #C1121F;
    --mj-alert-subtle: #FDF0D5;
    --mj-canvas: #FDFDFD;         /* editorial canvas (== --mj-surface) */
    --mj-surface-2: #F4F4F4;
    --mj-rule: #E5E5E5;           /* ruling line (== --mj-gray-200) */
    --mj-ink-900: #0A0A0A;        /* PERMANENT dark chrome surface (footer, demo-bar,
                                     dark chips, CTA banners). NEVER flips in dark mode —
                                     these surfaces are dark by design in BOTH themes.
                                     (--mj-black flips because it doubles as heading ink.) */

    /* Link-intent aliases — see design/identity/mashjoy.com-linking-system.md */
    --link-action: var(--mj-red);          /* affiliate / buy */
    --link-intelligence: var(--mj-trust);  /* source / authority */
    --link-trust: var(--mj-growth);        /* proof / verified */
    --link-structure: var(--mj-navy);      /* internal / navigation */

    /* Typographic System */
    --font-display: 'Technor', system-ui, sans-serif;
    --font-body: 'Canva Sans', Georgia, serif; /* Allow serif fallbacks for editorial feel if needed */
    --font-meta: 'Canva Sans Thin', system-ui, sans-serif;

    /* Layout & Spacing */
    --container-width: 1200px; /* Slightly narrower than SaaS */
    --article-width: 680px; /* Forbes/Verge strict reading width */
    
    --gap-xs: 8px;
    --gap-sm: 16px;
    --gap-md: 24px;
    --gap-lg: 48px;
    --gap-xl: 80px;

    /* Numeric spacing scale (4px base) — shared rhythm across chrome + inserts */
    --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px; --sp-5: 24px;
    --sp-6: 32px; --sp-7: 48px; --sp-8: 64px; --sp-9: 96px;

    /* SQUARE-FIRST geometry (Brand OS v3, 2026-07-27). The brand leans square; heavy
       rounding reads as generic/AI. Max 3px anywhere. Circles are banned — see .mj-sq. */
    --radius-sm: 1px;
    --radius-md: 2px;
    --radius-lg: 3px;
}

/* Brand name must NEVER render uppercase, even inside uppercased labels/badges (hard brand rule).
   Wrap the brand in <span class="brand">mashjoy.com</span> anywhere text-transform:uppercase applies. */
.brand { text-transform: none !important; }

/* ─────────────────────────────────────────────────────────────────────────
   DARK THEME — flip the SEMANTIC color tokens so every token-based element
   inverts automatically. This is the systemic fix for "text stays black in
   dark mode": the bug was that --mj-ink / --mj-black never changed. Responds
   to ALL toggle conventions used across prototypes (body.dark-mode / body.dark
   / [data-theme=dark]) so a class mismatch can't leave text unreadable.
   Brand red + accent greens/blues stay as-is (they read on dark). Adaptive
   logo dot: --logo-dot defaults red, flips per context.
   ───────────────────────────────────────────────────────────────────────── */
:root { --logo-dot: var(--mj-red); }
body.dark-mode, body.dark, :root[data-theme="dark"] {
  --mj-ink: #e7e7ea;
  --mj-black: #f5f5f7;
  --mj-charcoal: #d2d2d6;
  --mj-text-muted: #a4a4aa;
  --mj-gray-400: #8c8c92;
  --mj-gray-300: #55555b;
  --mj-gray-200: #2f2f34;
  --mj-rule: #2a2a2e;
  /* on a dark ground the ORIGINAL green is the readable one (7.25:1); the
     dark text green would fall to 3.43:1. The token flips, its uses do not. */
  --mj-growth-text: #10B981;
  --mj-surface: #141417;
  --mj-surface-alt: #0d0d0f;
  --mj-surface-2: #1c1c20;
  --mj-canvas: #0d0d0f;
  --mj-white: #141417;   /* neutral "paper" surfaces flip; true-white images keep their own bg */
}

/* ── ADAPTIVE WORDMARK LOGO COMPONENT ───────────────────────────────────────
   ONE inline-SVG horizontal wordmark replaces the separate light/dark image
   files across header, footer + article-header. Built from the brand-kit
   horizontal wordmark (viewBox 0 0 207.76 28.92). Three moving parts:
     .mj-logo__icon (red M square)  = FIXED brand red   (#ec2227, never flips)
     .mj-logo__m    (white M mark)  = FIXED white       (always reads on red)
     .mj-logo__wm   (wordmark text) = currentColor      → auto-adapts to theme
     .mj-logo__dot  (the .com dot)  = var(--logo-dot)   → red default; flips per
                                                           surface via modifiers
   Height is set on the SVG (default 26px); parent link controls layout.
   WordPress note: the dot maps to an ACF colour control / CSS var so the theme
   or a per-surface class can flip it without swapping asset files. */
.mj-logo-svg { display:block; height:26px; width:auto; color:var(--mj-black); }
.mj-logo-svg .mj-logo__icon { fill:#ec2227; }
.mj-logo-svg .mj-logo__m    { fill:#fff; }
.mj-logo-svg .mj-logo__wm   { fill:currentColor; }
/* NO RED DOT (Lucky, 2026-09-12): "the red dot version leans more into AI vs
   intentional black or white." The dot takes the wordmark's own ink, as in the brand
   pack's `mashjoy.com logo-light.svg` (all text #231f20) and `mashjoy.com-logo dark
   main.svg` (all text white, white square, red M). Same geometry as those files. */
.mj-logo-svg .mj-logo__dot  { fill:currentColor; }
/* Monochrome / theme-locked dot overrides */
.mj-logo-svg.is-dot-black { --logo-dot:#0a0a0a; }
.mj-logo-svg.is-dot-white { --logo-dot:#ffffff; }
/* Surface-locked wordmark colour (footers / permanently-dark or -light chrome) */
.mj-logo-svg.on-dark  { color:#f5f5f7; }
.mj-logo-svg.on-light { color:#0a0a0a; }

/* --- Base Resets --- */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { font-size: 16px; scroll-behavior: smooth; }
body {
    font-family: var(--font-body);
    background-color: var(--mj-surface);
    color: var(--mj-charcoal);
    line-height: 1.7; /* Increased for deep reading */
    -webkit-font-smoothing: antialiased;
}

/* --- Typography (Editorial Hierarchy) --- */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-display);
    color: var(--mj-black);
    line-height: 1.2;
    letter-spacing: -0.01em;
    font-weight: 800;
}

/* Tighter, more refined scaling */
h1 { font-size: clamp(2rem, 4vw, 3rem); margin-bottom: var(--gap-md); }
h2 { font-size: clamp(1.5rem, 3vw, 2rem); margin-bottom: var(--gap-sm); margin-top: var(--gap-lg); }
h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); margin-bottom: var(--gap-sm); }
h4 { font-family: var(--font-display); font-size: 1.125rem; font-weight: 800; }
h5 { font-family: var(--font-body); font-size: 0.875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--mj-red); }

p {
    margin-bottom: var(--gap-md);
    font-size: 1.125rem;
    color: var(--mj-charcoal);
}

.lead-text {
    font-size: 1.25rem;
    font-weight: 400;
    color: var(--mj-charcoal);
    line-height: 1.5;
    margin-bottom: var(--gap-lg);
}

.meta-text {
    font-family: var(--font-meta);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--mj-gray-400);
}

/* Masthead Logo */
.mj-brand-name {
    font-family: var(--font-display);
    font-weight: 800;
    color: var(--mj-black);
    font-size: 2rem;
    letter-spacing: -1px;
}
.mj-brand-name span { color: var(--mj-red); }

/* --- Linking Psychology --- */
a { text-decoration: none; transition: color 0.2s ease; }

/* Affiliate / Action Links - More subtle but distinct */
.link-action {
    color: var(--mj-charcoal);
    font-weight: 700;
    border-bottom: 1px solid var(--mj-red);
    box-shadow: inset 0 -4px 0 rgba(236, 34, 39, 0.2);
    transition: all 0.2s ease;
}
.link-action:hover { background: rgba(236, 34, 39, 0.1); }

/* Editorial Internal Links */
.link-hub {
    color: var(--mj-charcoal);
    font-weight: 700;
    border-bottom: 1px dotted var(--mj-charcoal);
}
.link-hub:hover { color: var(--mj-red); border-color: var(--mj-red); }

/* --- Layout System --- */
.e-con-full {
    width: 100%;
    padding: var(--gap-md) 4%;
}

.e-con-boxed {
    width: 100%;
    max-width: var(--container-width);
    margin: 0 auto;
}

/* Editorial Columns (Newsreader style) */
.grid-editorial { display: grid; grid-template-columns: 2fr 1fr; gap: var(--gap-lg); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-md); }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap-md); }

/* Article specific constraints */
.article-body {
    max-width: var(--article-width);
    margin: 0 auto;
}

/* --- Atomic UI Components (Integrated & Subtle) --- */

/* Buttons - Slimmer, less 'buy now' */
.btn {
    display: inline-flex; align-items: center; justify-content: center;
    padding: 10px 20px;
    font-family: var(--font-display); font-weight: 800; text-transform: uppercase; font-size: 0.875rem;
    border-radius: var(--radius-sm); cursor: pointer; border: none;
}
.btn-primary { background-color: var(--mj-ink-900); color: var(--mj-white); }
.btn-primary:hover { background-color: var(--mj-red); }
.btn-secondary { background-color: transparent; color: var(--mj-black); border: 1px solid var(--mj-gray-300); }

/* Integrated Product Box (The Verge style specs) */
.product-card {
    background: var(--mj-surface-alt);
    border: 1px solid var(--mj-gray-200);
    padding: var(--gap-md);
    margin-bottom: var(--gap-lg);
    display: flex; gap: var(--gap-md);
}
.product-card-visual { width: 120px; height: 120px; background: white; border: 1px solid var(--mj-gray-200); display: flex; align-items: center; justify-content: center; }
.product-card-content { flex: 1; }
.product-card h3 { margin-top: 0; margin-bottom: 4px; font-size: 1.25rem; }

/* Subtle Verdict Block */
.verdict-block {
    background: white;
    border-top: 4px solid var(--mj-black);
    border-bottom: 1px solid var(--mj-gray-200);
    padding: var(--gap-md) 0;
    margin: var(--gap-lg) 0;
}

/* Ruling Lines (Editorial Dividers) */
.ruling-line {
    border-top: 1px solid var(--mj-gray-200);
    margin: var(--gap-lg) 0;
}
.ruling-line-thick {
    border-top: 4px solid var(--mj-black);
    margin: var(--gap-md) 0;
}

/* Post Meta (Byline) */
.author-byline {
    display: flex; align-items: center; gap: 12px;
    border-top: 1px solid var(--mj-gray-200);
    border-bottom: 1px solid var(--mj-gray-200);
    padding: 12px 0; margin-bottom: var(--gap-lg);
}
.author-byline img { width: 40px; height: 40px; border-radius: 2px; }  /* G1: square, never a circle */

/* --- Responsive Overrides --- */
@media (max-width: 1024px) {
    .grid-editorial { grid-template-columns: 1fr; }
    .product-card { flex-direction: column; }
}
@media (max-width: 768px) {
    .grid-3, .grid-4 { grid-template-columns: 1fr; }
    .e-con-full { padding: var(--gap-sm) 5%; }
    h1 { font-size: 2rem; }
}

/* --- SYNCHRONIZED HEADER ARCHITECTURE CSS --- */

        /* Base Dashboard Styles */
        body { background-color: var(--mj-surface-alt); margin: 0; padding: 0; transition: background-color 0.3s, color 0.3s; }
        .dashboard-controls {
            position: fixed; top: 0; left: 0; right: 0; background: var(--mj-ink-900); color: white;
            padding: 12px 24px; display: flex; justify-content: center; gap: 24px; z-index: 10000;
            font-family: var(--font-display); font-size: 0.875rem; text-transform: uppercase;
        }
        .dashboard-controls button {
            background: transparent; color: white; border: 1px solid var(--mj-gray-400); 
            padding: 4px 12px; cursor: pointer; border-radius: var(--radius-sm); font-family: var(--font-display);
        }
        .dashboard-controls button.active { background: var(--mj-red); border-color: var(--mj-red); font-weight: bold; }
        
        .header-showcase { margin-top: 60px; padding: 40px; display: flex; flex-direction: column; gap: 80px; }
        .showcase-label { font-family: var(--font-meta); margin-bottom: 16px; color: var(--mj-gray-400); letter-spacing: 2px; }

        /* Header Architecture Base */
        .mj-header {
            width: 100%; border-bottom: 1px solid var(--mj-gray-200);
            background: var(--mj-surface); transition: all 0.3s;
            position: relative;
        }
        
        /* 3-Column Layout for seamless Horizontal/Vertical transitions */
        .mj-header-top {
            max-width: 1400px; margin: 0 auto; padding: 0 24px; height: 80px;
            display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 24px;
        }
        
        /* Dark Mode overrides */
        body.dark-mode .mj-header { background: #111111; border-color: #333; color: white; }
        body.dark-mode .nav-links a { color: white; }
        body.dark-mode .nav-links a:hover { color: var(--mj-red); }
        body.dark-mode .btn-primary { background: white; color: black; }
        body.dark-mode .btn-primary:hover { background: var(--mj-red); color: white; }
        body.dark-mode .mega-menu-container { background: #111111; border-color: #333; color: white; }
        body.dark-mode .mm-title { color: white; border-bottom-color: #444; }
        body.dark-mode .mm-list a { color: #ccc; }
        body.dark-mode .mm-list a:hover { color: white; }
        body.dark-mode .mm-article-card { color: #ccc; }
        body.dark-mode .mm-article-card h4 { color: white; }

        /* Left Group (Hamburger) */
        .header-left { display: flex; align-items: center; justify-content: flex-start; }
        
        /* Logo Container */
        .header-logo { display: flex; align-items: center; justify-content: flex-start; transition: justify-content 0.3s; }
        
        /* Right Group (Search, Dark Mode, Subscribe) */
        .header-center { flex: 2; display: flex; justify-content: center; }
        .header-right { flex: 1; display: flex; justify-content: flex-end; align-items: center; gap: 4px; }
        .header-right .btn { margin-left: 16px; }
        
        .header-icon { background: none; border: none; cursor: pointer; padding: 4px; display: flex; align-items: center; color: var(--mj-black); transition: color 0.3s; }
        .header-icon svg { width: 22px; height: 22px; stroke: currentColor; stroke-width: 1.5; fill: none; }
        body.dark-mode .header-icon { color: white; }
        .header-icon:hover { color: var(--mj-red) !important; }
        
        /* Logos - Exact Brand Kit SVGs */
        .mj-logo-img { display: none; transition: all 0.3s; }
        body.logo-horizontal .logo-horiz-img { height: 28px; }
        body.logo-vertical .logo-vert-img { height: 112px; }

        /* Theme Rendering Logic */
        body.light-mode.logo-horizontal .logo-horiz-img.logo-for-light { display: block; }
        body.light-mode.logo-vertical .logo-vert-img.logo-for-light { display: block; }
        body.dark-mode.logo-horizontal .logo-horiz-img.logo-for-dark { display: block; }
        body.dark-mode.logo-vertical .logo-vert-img.logo-for-dark { display: block; }

        /* Navigation (Horizontal Layout) */
        body.logo-horizontal .mj-header-top { display: flex; justify-content: space-between; }
        body.logo-horizontal .header-logo { margin-right: 32px; }
        body.logo-horizontal .header-center { flex: 1; display: flex; justify-content: flex-start; }
        
        /* Navigation (Vertical Layout Two-Tier) */
        body.logo-vertical .mj-header-top { 
            height: auto; 
            display: grid; 
            grid-template-columns: 1fr auto 1fr; 
            grid-template-areas: 
                "left logo right"
                "nav nav nav";
            padding-top: 0;
            row-gap: 0;
        }
        body.logo-vertical .header-left { grid-area: left; align-items: center; }
        body.logo-vertical .header-logo { grid-area: logo; justify-content: center; margin-right: 0; padding-top: 16px; padding-bottom: 16px; }
        body.logo-vertical .header-right { grid-area: right; align-items: center; }
        body.logo-vertical .header-center { 
            grid-area: nav; display: flex; justify-content: center; 
            width: 100%; border-top: 1px solid var(--mj-gray-200); 
        }
        body.dark-mode.logo-vertical .header-center { border-top-color: #333; }

        .nav-links { display: flex; gap: 32px; list-style: none; margin: 0; padding: 0; height: 100%; }
        .nav-links > li { display: flex; align-items: center; }
        .nav-links > li.has-dropdown { position: relative; }
        .nav-links > li:has(.mm-dropdown-anchor) { position: relative; }
        /* Sync height of nav items to parent */
        body.logo-horizontal .nav-links > li { height: 80px; }
        body.logo-vertical .nav-links > li { height: 50px; }
        
        .nav-links a { 
            font-family: var(--font-display); font-weight: 500; font-size: 0.9rem;
            color: var(--mj-black); text-decoration: none; text-transform: uppercase;
            display: flex; align-items: center; gap: 6px;
        }
        .nav-links a span { font-size: 0.7rem; opacity: 0.6; }
        .nav-links a:hover { color: var(--mj-red); }

        /* -------------------------------------------
           MEGA MENUS BASE
           ------------------------------------------- */
        .mega-menu-container {
            position: absolute; left: 0; width: 100%; top: 100%;
            background: var(--mj-surface);
            border-bottom: 1px solid var(--mj-gray-200);
            box-shadow: 0 10px 30px rgba(0,0,0,0.05);
            opacity: 0; visibility: hidden; transform: translateY(-10px);
            transition: all 0.2s ease-in-out;
            z-index: 100;
        }
        body.dark-mode .mega-menu-container { background: #111111; border-color: #333; color: white; }
        
        .nav-links > li:hover .mega-menu-container { opacity: 1; visibility: visible; transform: translateY(0); }
        .mega-menu-container.hamburger-active { opacity: 1; visibility: visible; transform: translateY(0); }
        
        .mm-title { font-family: var(--font-display); font-size: 1.1rem; text-transform: uppercase; letter-spacing: 1px; border-bottom: 1px solid var(--mj-gray-200); padding-bottom: 8px; margin: 0 0 16px 0; font-weight: 600; }
        
        .mm-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 16px; }
        .mm-list a { font-family: var(--font-body); font-size: 1.05rem; text-transform: none; color: var(--mj-charcoal); font-weight: 400; display: inline-block; transition: transform 0.2s, color 0.2s; }
        .mm-list a:hover { color: var(--mj-red); transform: translateX(4px); }
        .mm-list.bold-list a { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; font-size: 0.95rem; letter-spacing: 0; }

        /* 1. Fullscreen Forbes Style */
        .mm-full { padding: 60px 40px; height: calc(100vh - 120px); overflow-y: auto; display: grid; grid-template-columns: 1fr 340px; gap: 60px; max-width: 1500px; margin: 0 auto; border-top: 1px solid var(--mj-gray-200); }
        .mm-links-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px 60px; align-content: start; }
        @media (min-width: 1400px) { .mm-links-grid { grid-template-columns: repeat(4, 1fr); } }
        body.dark-mode .mm-full { border-top-color: #333; }
        .mm-column { display: flex; flex-direction: column; gap: 16px; }
        .mm-sidebar { border-left: 1px solid var(--mj-gray-200); padding-left: 40px; display: flex; flex-direction: column; gap: 24px; }
        body.dark-mode .mm-sidebar { border-left-color: #333; }
        .mm-featured-article { display: block; text-decoration: none; width: 100%; }
        .mm-featured-article img { width: 100%; aspect-ratio: 16/9; object-fit: cover; border-radius: var(--radius-sm); margin-bottom: 12px; display: block; }
        .mm-featured-article h4 { font-family: var(--font-display); font-size: 1.2rem; line-height: 1.3; font-weight: 700; margin: 0 0 8px 0; color: var(--mj-black); text-wrap: balance; }
        body.dark-mode .mm-featured-article h4 { color: white; }
        .mm-newsletter-box { background: var(--mj-gray-100); padding: 24px; border-radius: var(--radius-sm); margin-top: auto; }
        body.dark-mode .mm-newsletter-box { background: #1a1a1a; }
        .mm-newsletter-box h4 { font-family: var(--font-display); margin: 0 0 8px 0; font-size: 1.1rem; }
        .mm-newsletter-box p { font-size: 0.9rem; margin: 0; opacity: 0.8; }
        
        /* 2. Newsreader Showcase Style */
        .mm-showcase { padding: 40px; max-width: 1200px; margin: 0 auto; display: grid; grid-template-columns: 220px 2fr 1.5fr; gap: 40px; border-top: 1px solid var(--mj-gray-200); }
        body.dark-mode .mm-showcase { border-top-color: #333; }
        .mm-trending-main { position: relative; border-radius: var(--radius-sm); overflow: hidden; height: 100%; min-height: 320px; display: block; }
        .mm-trending-main img { width: 100%; height: 100%; object-fit: cover; position: absolute; top:0; left:0; transition: transform 0.5s; }
        .mm-trending-main:hover img { transform: scale(1.05); }
        .mm-trending-overlay { position: absolute; bottom: 0; left: 0; width: 100%; padding: 60px 24px 24px; background: linear-gradient(to top, rgba(0,0,0,0.9), transparent); color: white; }
        .mm-trending-overlay h3 { font-family: var(--font-display); font-size: 1.6rem; font-weight: 800; margin: 0 0 8px 0; line-height: 1.2; color: #ffffff !important; }
        .mm-trending-overlay span { font-size: 0.85rem; font-family: var(--font-body); opacity: 0.8; color: #ffffff !important; }
        
        .mm-popular-list { display: flex; flex-direction: column; gap: 20px; }
        .mm-popular-item { display: flex; gap: 16px; align-items: center; text-decoration: none; color: var(--mj-black); }
        body.dark-mode .mm-popular-item { color: white; }
        .mm-popular-item img { width: 90px; height: 90px; object-fit: cover; border-radius: var(--radius-sm); }
        .mm-popular-item h4 { font-family: var(--font-display); font-size: 1.05rem; font-weight: 700; margin: 0 0 6px 0; line-height: 1.3; transition: color 0.2s; }
        .mm-popular-item:hover h4 { color: var(--mj-red); }
        .mm-popular-item span { font-size: 0.8rem; opacity: 0.7; }
        
        /* 3. Newsreader Simple Dropdown */
        .mm-dropdown-anchor { 
            position: absolute; left: 50%; transform: translateX(-50%) translateY(10px); 
            width: auto; min-width: 220px; 
            background: var(--mj-surface); 
            border: 1px solid var(--mj-gray-200); 
            border-radius: var(--radius-sm); 
            box-shadow: 0 10px 30px rgba(0,0,0,0.08);
            z-index: 200;
        }
        body.dark-mode .mm-dropdown-anchor { background: #111111; border-color: #333; }
        .nav-links > li:hover .mm-dropdown-anchor { transform: translateX(-50%) translateY(0); }
        
        .mm-simple { padding: 20px 24px; }
        .mm-simple .mm-list { gap: 12px; }
        
        /* 4. Newsreader Visual Grid */
        .mm-visual-grid { padding: 40px; max-width: 1200px; margin: 0 auto; display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; border-top: 1px solid var(--mj-gray-200); }
        body.dark-mode .mm-visual-grid { border-top-color: #333; }
        .mm-grid-card { position: relative; border-radius: var(--radius-sm); overflow: hidden; aspect-ratio: 4/3; display: block; }
        .mm-grid-card img { width: 100%; height: 100%; object-fit: cover; position: absolute; top:0; left:0; transition: transform 0.4s; }
        .mm-grid-card:hover img { transform: scale(1.05); }
        .mm-grid-overlay { position: absolute; bottom: 0; left: 0; width: 100%; padding: 40px 20px 20px; background: linear-gradient(to top, rgba(0,0,0,0.9), transparent); color: white; }
        .mm-grid-overlay h4 { font-family: var(--font-display); font-size: 1.15rem; font-weight: 700; margin: 0; line-height: 1.3; color: #ffffff !important; text-wrap: balance; }

        /* Progress Bar for Single Header */
        .reading-progress-container { position: absolute; bottom: -1px; left: 0; width: 100%; height: 2px; background: transparent; }
        .reading-progress-bar { height: 100%; width: 45%; background: var(--mj-red); }
    

/* --- EDITORIAL BLOCKS (Forbes / Newsreader Rebuild) --- */
.mj-header {
    position: sticky;
    top: 0;
    z-index: 1000;
}

/* Single Article specific */
.article-header-border { border-top: 2px solid var(--mj-red); width: 100%; display: block; margin-bottom: 48px; }
.article-meta-cat { color: var(--mj-red); font-size: 0.8rem; font-weight: 800; text-transform: uppercase; letter-spacing: 1px; display: block; text-align: center; margin-bottom: 16px; }
.article-h1 { font-family: var(--font-display); font-size: 3.5rem; font-weight: 800; text-align: center; line-height: 1.1; margin: 0 auto 24px auto; max-width: 900px; letter-spacing: -1px; }
.article-sub { font-family: var(--font-serif); font-size: 1.5rem; text-align: center; max-width: 800px; margin: 0 auto 48px auto; color: var(--mj-charcoal); line-height: 1.5; }

.author-block { display: flex; align-items: center; justify-content: center; gap: 16px; padding-top: 24px; padding-bottom: 24px; border-top: 1px solid var(--mj-gray-200); border-bottom: 1px solid var(--mj-gray-200); max-width: 800px; margin: 0 auto 64px auto; }
.author-avatar { width: 50px; height: 50px; border-radius: 2px; object-fit: cover; background: var(--mj-surface-alt); }
.author-info strong { display: block; font-size: 1rem; }
.author-info span { font-size: 0.85rem; color: var(--mj-gray-400); }

.article-body { max-width: 800px; margin: 0 auto; font-family: var(--font-serif); font-size: 1.25rem; line-height: 1.8; color: var(--mj-black); }
.article-body p { margin-bottom: 24px; }
.article-body h2 { font-family: var(--font-display); font-size: 2.25rem; font-weight: 800; margin: 64px 0 24px 0; letter-spacing: -0.5px; }

.data-card { background: var(--mj-surface); border: 1px solid var(--mj-gray-200); padding: 48px; margin: 48px 0; border-radius: var(--radius-sm); box-shadow: 0 4px 12px rgba(0,0,0,0.03); }
.data-card h3 { font-family: var(--font-display); font-size: 1.5rem; margin-top: 0; margin-bottom: 8px; }
.data-card p.meta { font-size: 0.8rem; color: var(--mj-gray-400); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 32px; }
.bar-chart-row { display: flex; align-items: center; gap: 16px; margin-bottom: 24px; }
.bar-label { width: 120px; font-size: 0.9rem; color: var(--mj-gray-400); }
.bar-track { flex: 1; height: 8px; background: var(--mj-surface-alt); border-radius: 4px; position: relative; }
.bar-fill { position: absolute; left: 0; top: 0; height: 100%; border-radius: 4px; }
.bar-fill.red { background: var(--mj-red); }
.bar-fill.black { background: var(--mj-ink-900); }
.bar-value { width: 40px; font-weight: 800; font-family: var(--font-display); font-size: 1rem; text-align: right; }

/* Homepage specific blocks */
.block-header { display: flex; justify-content: space-between; align-items: flex-end; border-bottom: 2px solid var(--mj-black); padding-bottom: 8px; margin-bottom: 24px; margin-top: 64px; }
.block-title { font-family: var(--font-display); font-size: 1.25rem; text-transform: uppercase; font-weight: 800; margin: 0; }

.hero-block { display: grid; grid-template-columns: 2fr 1fr; gap: 48px; margin-top: 48px; }
@media (max-width: 1024px) { .hero-block { grid-template-columns: 1fr; } }
.hero-main img { width: 100%; height: 500px; object-fit: cover; margin-bottom: 16px; }
.hero-main h2 { font-size: 2.5rem; font-family: var(--font-display); margin: 8px 0; line-height: 1.2; }
.hero-side-item { display: flex; gap: 16px; margin-bottom: 24px; padding-bottom: 24px; border-bottom: 1px solid var(--mj-gray-200); }
.hero-side-item img { width: 100px; height: 100px; object-fit: cover; border-radius: 4px; }
.hero-side-item h4 { font-size: 1.1rem; line-height: 1.3; margin: 4px 0; }

.grid-4-block { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }
.card-standard img { width: 100%; height: 180px; object-fit: cover; margin-bottom: 12px; border-radius: 4px; }
.card-standard h4 { font-size: 1.25rem; margin: 8px 0; line-height: 1.3; }

.cta-banner { background: var(--mj-ink-900); color: white; padding: 64px; text-align: center; margin: 64px 0; }
.cta-banner h2 { font-size: 2.5rem; margin-bottom: 16px; color: white; }

.list-block { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; }
.list-item { display: flex; gap: 16px; margin-bottom: 16px; align-items: flex-start; }
.list-item-num { font-family: var(--font-display); font-size: 1.5rem; font-weight: 800; color: var(--mj-gray-300); width: 24px; }

/* --- NEWSREADER V2 ARCHITECTURE --- */

/* Unified Section Spacing */
.nr-section { margin: 64px 0; }
.nr-section-header { 
    display: flex; align-items: center; justify-content: space-between; 
    border-bottom: 2px solid var(--mj-black); padding-bottom: 8px; margin-bottom: 32px; 
}
.nr-section-title { font-family: var(--font-display); font-size: 1.5rem; text-transform: uppercase; font-weight: 800; margin: 0; }
.nr-badge { 
    display: inline-block; padding: 4px 8px; font-size: 0.7rem; font-weight: 800; 
    text-transform: uppercase; letter-spacing: 0.5px; border-radius: 2px; margin-bottom: 8px; 
}
.nr-badge.red { background: var(--mj-red); color: white; }
.nr-badge.green { background: #008f5a; color: white; } /* Newsreader green */
.nr-badge.dark { background: var(--mj-ink-900); color: white; }

/* Global Article Typography Overrides for NR */
.nr-card h3, .nr-card h4 { font-family: var(--font-display); line-height: 1.25; margin: 8px 0; font-weight: 800; letter-spacing: -0.5px; }
.nr-card p { font-family: var(--font-serif); font-size: 0.95rem; color: var(--mj-charcoal); line-height: 1.5; margin: 0; }
.nr-card .meta { font-size: 0.8rem; color: var(--mj-gray-400); margin-top: 8px; }

/* Hover effects */
.nr-card img { transition: transform 0.4s ease; }
.nr-card:hover img { transform: scale(1.03); }
.img-wrapper { overflow: hidden; border-radius: 4px; display: block; margin-bottom: 12px; }

/* 1. Hero (60/40) */
.nr-hero { display: grid; grid-template-columns: 1.5fr 1fr; gap: 48px; }
@media (max-width: 1024px) { .nr-hero { grid-template-columns: 1fr; } }
.nr-hero-main { position: relative; }
.nr-hero-main .img-wrapper { height: 500px; margin-bottom: 0; }
.nr-hero-main img { width: 100%; height: 100%; object-fit: cover; }
.nr-hero-main-content { 
    position: absolute; bottom: 0; left: 0; right: 0; padding: 48px; 
    background: linear-gradient(transparent, rgba(0,0,0,0.9)); color: white; 
}
.nr-hero-main-content h2 { font-size: 2.5rem; color: white; font-family: var(--font-display); line-height: 1.1; margin: 8px 0; }
.nr-hero-main-content p { font-family: var(--font-serif); font-size: 1.1rem; color: #ddd; }

.nr-hero-side { display: flex; flex-direction: column; gap: 24px; }
.nr-hero-side-item { display: flex; gap: 16px; align-items: center; border-bottom: 1px solid var(--mj-gray-200); padding-bottom: 24px; }
.nr-hero-side-item:last-child { border-bottom: none; }
.nr-hero-side-item .img-wrapper { width: 120px; height: 120px; flex-shrink: 0; margin-bottom: 0; }
.nr-hero-side-item img { width: 100%; height: 100%; object-fit: cover; }
.nr-hero-side-item h4 { font-size: 1.15rem; margin: 4px 0; }

/* 2. Apps (50/50) */
.nr-apps { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; }
.nr-apps-main .img-wrapper { height: 350px; }
.nr-apps-main img { width: 100%; height: 100%; object-fit: cover; }
.nr-apps-main h3 { font-size: 1.8rem; }
.nr-apps-stack { display: flex; flex-direction: column; gap: 32px; }
.nr-apps-item { display: flex; gap: 24px; align-items: flex-start; }
.nr-apps-item .img-wrapper { width: 160px; height: 120px; flex-shrink: 0; margin-bottom: 0; }
.nr-apps-item img { width: 100%; height: 100%; object-fit: cover; }

/* 3. Most Popular (Asymmetric 4-Col) */
.nr-popular { display: grid; grid-template-columns: 1.2fr 1fr 1fr 1fr; gap: 32px; }
@media (max-width: 1024px) { .nr-popular { grid-template-columns: 1fr 1fr; } }
@media (max-width: 768px) { .nr-popular { grid-template-columns: 1fr; } }
.nr-pop-lead .img-wrapper { height: 250px; }
.nr-pop-lead img { width: 100%; height: 100%; object-fit: cover; }
.nr-pop-lead h3 { font-size: 1.5rem; }
.nr-pop-col { display: flex; flex-direction: column; gap: 24px; }
.nr-pop-item-border { border-bottom: 1px solid var(--mj-gray-200); padding-bottom: 24px; }

/* 4. Hardware (Premium 2-Col) */
.nr-hardware { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; }
.nr-hw-card .img-wrapper { height: 350px; }
.nr-hw-card img { width: 100%; height: 100%; object-fit: cover; }
.nr-hw-card h3 { font-size: 2rem; margin: 16px 0; }

/* 5. CTAs */
.nr-cta-banner { 
    background: var(--mj-ink-900); color: white; padding: 64px 48px; 
    border-radius: 8px; display: flex; align-items: center; justify-content: space-between; 
}
.nr-cta-content h2 { font-size: 2.25rem; margin: 0 0 16px 0; color: white; font-family: var(--font-display); }
.nr-cta-content p { font-size: 1.1rem; color: #ccc; margin: 0; max-width: 500px; font-family: var(--font-serif); }
.nr-cta-action input { padding: 16px 24px; width: 300px; border: none; border-radius: 4px; margin-right: 8px; }
.nr-cta-action button { background: var(--mj-red); color: white; border: none; padding: 16px 32px; font-weight: 800; border-radius: 4px; cursor: pointer; }

/* 6. Trending (3-Col) */
.nr-trending { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; }
.nr-trend-card .img-wrapper { height: 200px; }
.nr-trend-card img { width: 100%; height: 100%; object-fit: cover; }
.nr-trend-card h4 { font-size: 1.25rem; }

/* 7. Social / Video */
.nr-social { display: grid; grid-template-columns: 1fr 1.5fr; gap: 48px; }
.nr-video-lead .img-wrapper { height: 400px; position: relative; }
.nr-video-lead img { width: 100%; height: 100%; object-fit: cover; }
.nr-play-btn { 
    position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); 
    width: 64px; height: 64px; background: rgba(255,255,255,0.95); border-radius: 2px; 
    display: flex; align-items: center; justify-content: center; color: var(--mj-red); font-size: 24px; 
}
.nr-social-col { display: flex; flex-direction: column; gap: 24px; }
.nr-social-item { display: flex; gap: 16px; }
.nr-social-item .img-wrapper { width: 140px; height: 100px; flex-shrink: 0; position: relative; }
.nr-social-item img { width: 100%; height: 100%; object-fit: cover; }
.nr-social-item .nr-play-btn-small { 
    position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); 
    width: 32px; height: 32px; background: rgba(255,255,255,0.95); border-radius: 2px; display: flex; align-items: center; justify-content: center; font-size: 12px; color: var(--mj-red);
}

/* 8. AI Intelligence (Dark Mode Block) */
.nr-ai-block { background: var(--mj-ink-900); padding: 64px 48px; border-radius: 8px; color: white; margin: 64px 0; }
.nr-ai-block .nr-section-header { border-bottom-color: #444; }
.nr-ai-block .nr-section-title { color: white; }
.nr-ai-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; }
.nr-ai-card .img-wrapper { height: 280px; }
.nr-ai-card img { width: 100%; height: 100%; object-fit: cover; }
.nr-ai-card h3 { color: white; font-size: 1.75rem; }
.nr-ai-card p { color: #aaa; }

/* 9. Commerce (4-Col) */
.nr-commerce { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }
.nr-comm-card { text-align: center; border: 1px solid var(--mj-gray-200); padding: 24px; border-radius: 8px; transition: transform 0.3s; }
.nr-comm-card:hover { transform: translateY(-5px); box-shadow: 0 10px 20px rgba(0,0,0,0.05); }
.nr-comm-card img { width: 120px; height: 120px; object-fit: contain; margin: 0 auto 16px auto; }
.nr-comm-card h4 { font-size: 1.1rem; margin-bottom: 8px; }
.nr-price { font-weight: 800; color: var(--mj-red); font-size: 1.25rem; display: block; margin-bottom: 16px; }
.nr-comm-card .btn { width: 100%; display: block; }

/* --- POLISH V3 FIXES --- */
.nr-section a { color: inherit; text-decoration: none; }
.nr-badge.green { background: var(--mj-charcoal); color: white; } /* Reverted green to Mashjoy Charcoal */
.nr-card a:hover { color: var(--mj-red); }

/* V3 Header Polish */
.bold-list a { font-weight: 600 !important; }
.header-right { gap: 4px !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   BRAND OS v3 — "BRING THE BRAND TO LIFE" LIFT   (Opus 5, 2026-07-27)
   Spec: systems/smartcms-design-directives-v2.md
   Additive + namespaced. Existing approved layouts/headings are untouched.
   ═══════════════════════════════════════════════════════════════════════════ */

/* --- The mashjoy square: our marker primitive. Replaces dots/circles/bullets. --- */
.mj-sq { display:inline-block; width:.62em; height:.62em; background:var(--mj-red);
  border-radius:1px; flex:0 0 auto; vertical-align:baseline; }
.mj-sq--green{ background:var(--mj-growth) } .mj-sq--blue{ background:var(--mj-trust) }
.mj-sq--ink{ background:var(--mj-ink-900) } .mj-sq--lg{ width:1em;height:1em }

/* --- Brand bullets (D6 + §6 refinement). The SOLID SQUARE (.mj-sq) is the UI/brand
   marker (labels, kickers, "find your match"). TEXT bullets are rounded coloured dots
   in black/red/blue — distinct from the hard square so the two never read the same. --- */
.mj-list{ list-style:none; margin:0 0 var(--sp-5); padding:0 }
.mj-list li{ position:relative; padding-left:1.6em; margin-bottom:var(--sp-3); line-height:1.65 }
.mj-list li::before{ content:""; position:absolute; left:0; top:.52em;
  width:.5em; height:.5em; background:var(--mj-red); border-radius:1px }   /* D6/G6: the mashjoy SQUARE, never a disc */
.mj-list--green li::before{ background:var(--mj-growth) }
.mj-list--blue  li::before{ background:var(--mj-trust) }
.mj-list--ink   li::before{ background:var(--mj-ink-900) }                    /* black dot */
/* numbered/ranked variant — square index chips (UI marker → stays square), black or red */
.mj-list--steps{ counter-reset:mjstep }
.mj-list--steps li{ padding-left:2.6em; min-height:1.8em }
.mj-list--steps li::before{ counter-increment:mjstep; content:counter(mjstep);
  width:1.7em; height:1.7em; top:.05em; background:var(--mj-ink-900); color:#fff;
  border-radius:2px; display:flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; font-size:.72em }
.mj-list--steps.mj-list--red li::before{ background:var(--mj-red) }           /* red numbered */

/* --- Brand mark (E2/G4) — REBUILT 2026-07-30 from the real logomark
   (assets/media/logos/mashjoy-logomark--red-white.svg, viewBox 0 0 110.25 110.25).
   The previous mark used derived artwork copied from elsewhere and always forced the
   "mashjoy.com" text lockup onto every table header, which over-branded the article.

   RULES:
     • The M ICON ALONE is the default. Text is opt-in (.mj-attrib--wordmark), for
       infographics and standalone graphics that travel off-site.
     • Red or black ground — pick whichever sits better against the element.
     • Left OR right (.mj-attrib--left). Not reflexively right.
     • Use it where a screenshot would travel. Not on every element with a header. --- */
.mj-mark{ display:inline-block; width:16px; height:16px; border-radius:1px; flex:0 0 auto;
  background-size:contain; background-repeat:no-repeat; background-position:center;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 110.25 110.25'%3E%3Crect width='110.25' height='110.25' fill='%23ec2227'/%3E%3Cpolygon points='83.11 20.13 82.48 20.13 68.22 50.72 68.22 50.89 68.14 50.89 53.05 83.25 49.8 90.22 64.77 90.22 68.02 83.25 68.22 83.25 68.22 82.83 83.11 50.89 83.11 90.22 96.68 90.22 96.68 50.89 96.68 21.8 96.68 20.13 83.11 20.13' fill='%23fff'/%3E%3Cpath d='M69.7,20.03h-15.67l-1.74,3.02-16.02,27.74-22.71,39.33h15.67c7.68-13.31,15.37-26.62,23.05-39.94v22.84l10.37-22.23,14.34-30.76h-7.3Z' fill='%23fff'/%3E%3C/svg%3E") }
.mj-mark--ink{ background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 110.25 110.25'%3E%3Crect width='110.25' height='110.25' fill='%23000'/%3E%3Cpolygon points='83.11 20.13 82.48 20.13 68.22 50.72 68.22 50.89 68.14 50.89 53.05 83.25 49.8 90.22 64.77 90.22 68.02 83.25 68.22 83.25 68.22 82.83 83.11 50.89 83.11 90.22 96.68 90.22 96.68 50.89 96.68 21.8 96.68 20.13 83.11 20.13' fill='%23fff'/%3E%3Cpath d='M69.7,20.03h-15.67l-1.74,3.02-16.02,27.74-22.71,39.33h15.67c7.68-13.31,15.37-26.62,23.05-39.94v22.84l10.37-22.23,14.34-30.76h-7.3Z' fill='%23fff'/%3E%3C/svg%3E") }
.mj-mark--lg{ width:22px; height:22px }
/* the attribution slot: icon by default, margin-left auto so it parks opposite the title */
.mj-attrib{ display:inline-flex; align-items:center; gap:7px; margin-left:auto;
  font-family:var(--font-meta); font-size:.6rem; letter-spacing:.4px; text-transform:none;
  opacity:.9 }
.mj-attrib--left{ margin-left:0; margin-right:auto }
.mj-attrib--wordmark::after{ content:"mashjoy.com" }      /* opt-in only */

.mj-tbl{ width:100%; border-collapse:collapse; font-size:.95rem;
  border:1px solid var(--mj-rule); background:var(--mj-surface) }
.mj-tbl thead th{ background:var(--mj-ink-900); color:#fff; text-align:left;
  font-family:var(--font-display); font-weight:800; font-size:.76rem; letter-spacing:.8px;
  text-transform:uppercase; padding:14px 16px; border:none }
.mj-tbl--red thead th{ background:var(--mj-red) }
.mj-tbl--navy thead th{ background:var(--mj-navy) }
.mj-tbl th, .mj-tbl td{ padding:13px 16px; border-bottom:1px solid var(--mj-rule);
  vertical-align:top }
.mj-tbl tbody tr:last-child td{ border-bottom:none }
.mj-tbl tbody tr:nth-child(even){ background:var(--mj-surface-2) }
.mj-tbl tbody tr:hover{ background:var(--mj-growth-subtle) }
body.dark-mode .mj-tbl tbody tr:hover{ background:rgba(16,185,129,.10) }
.mj-tbl .is-win{ color:var(--mj-growth-text); font-weight:700 }
/* table caption bar carries the attribution mark */
.mj-tbl-cap{ display:flex; align-items:center; gap:10px; padding:10px 2px;
  font-family:var(--font-meta); font-size:.7rem; letter-spacing:1.6px;
  text-transform:uppercase; color:var(--mj-gray-400) }

/* --- Buttons that POP (E4). Every state changes visibly. --- */
.mj-btn{ display:inline-flex; align-items:center; justify-content:center; gap:9px;
  font-family:var(--font-display); font-weight:800; font-size:.83rem; letter-spacing:.4px;
  text-transform:uppercase; padding:14px 24px; border-radius:var(--radius-md);
  border:2px solid transparent; text-decoration:none; cursor:pointer;
  transition:background .16s ease, color .16s ease, border-color .16s ease, transform .12s ease }
.mj-btn:active{ transform:translateY(1px) }
.mj-btn--primary{ background:var(--mj-red); color:#fff; border-color:var(--mj-red) }
.mj-btn--primary:hover{ background:var(--mj-red-hover); border-color:var(--mj-red-hover); color:#fff }
/* secondary is a REAL action, not a ghost whisper — it fills on hover */
.mj-btn--secondary{ background:transparent; color:var(--mj-ink); border-color:var(--mj-ink) }
.mj-btn--secondary:hover{ background:var(--mj-ink-900); color:#fff; border-color:var(--mj-ink-900) }
body.dark-mode .mj-btn--secondary{ color:var(--mj-ink); border-color:var(--mj-gray-300) }
body.dark-mode .mj-btn--secondary:hover{ background:#fff; color:#0a0a0a; border-color:#fff }
/* inverted — for use ON solid red/black CTA panels (E7) */
.mj-btn--invert{ background:#fff; color:var(--mj-ink-900); border-color:#fff }
.mj-btn--invert:hover{ background:transparent; color:#fff; border-color:#fff }
.mj-btn--invert-outline{ background:transparent; color:#fff; border-color:rgba(255,255,255,.55) }
.mj-btn--invert-outline:hover{ background:#fff; color:var(--mj-ink-900); border-color:#fff }

/* --- `.cta-btn` — THE CTA BLOCK'S BUTTON. Rescued from a prototype <style>.
   It was defined ONLY inside mashjoy-cta-block.html, so the library had the two
   scoped modifiers (`width:100%`, the `.alt` outline) and none of the button
   itself. On the site every CTA in the mid-funnel element therefore rendered as
   16px body text on a transparent ground — Lucky: "the CTA buttons in those
   sections are text and I also just wanted to ask for buttons instead."

   THE FONT IS THE POINT. A button that borrows body type stops reading as a
   control, which is the whole complaint: "make the buttons also use the techno
   font like other buttons so that the buttons still feel like buttons all the
   time." `--font-display` is that font, and it is what `.mj-btn` and the footer
   ribbon already use. --- */
.cta-btn{ display:inline-flex; align-items:center; justify-content:center; gap:8px;
  font-family:var(--font-display); font-weight:800; text-transform:uppercase;
  font-size:.85rem; letter-spacing:.3px; padding:13px 24px;
  background:var(--link-action); color:#fff; border:none;
  border-radius:var(--radius-sm); cursor:pointer; text-decoration:none;
  white-space:nowrap; transition:background .18s ease, color .18s ease }
.cta-btn:hover{ background:var(--mj-red-hover); color:#fff }
.cta-btn:focus-visible{ outline:2px solid var(--mj-black); outline-offset:2px }
body.dark-mode .cta-btn:focus-visible{ outline-color:#fff }
.cta-btn--ghost{ background:transparent; color:var(--mj-black);
  border:1px solid var(--mj-gray-300) }
body.dark-mode .cta-btn--ghost{ color:#fff; border-color:#444 }
.cta-btn--ghost:hover{ border-color:var(--link-action); color:var(--link-action);
  background:transparent }
body.dark-mode .cta-btn--ghost:hover{ color:#ff6b6f; border-color:#ff6b6f }
/* THE DISCLOSURE IS THE SMALLEST TYPE ON THE SITE AND STAYS THERE. Asked to
   make this smaller, the honest answer is that .66rem is the bottom step of the
   scale — the review badges and every other micro-label sit on it — and a
   disclosure that is harder to read than the price it qualifies is not a
   disclosure. What actually read as "too big" was its FOOTPRINT: 1.7 line-height
   and .4px tracking spread 146 characters over two airy lines that occupied as
   much room as the button. Same size, tighter set: it lands as one quiet caption
   block under the action instead of a second paragraph.
   (A 52ch measure was tried here and made it WORSE — at this size the cap is
   346px, which pushed 146 characters onto a third line. Micro-type wants the
   full width of its panel; the tightening is what buys the room back.) */
.cta-disc{ font-family:var(--font-meta); font-size:.66rem; letter-spacing:.2px;
  line-height:1.45; color:var(--mj-gray-400); margin:6px 0 0 }
.cta-disc b{ color:var(--mj-text-muted) }
body.dark-mode .cta-disc b{ color:#b6b6bb }

/* --- Callout / Quick Answer (D5): substantial brand bar, real colour. --- */
.mj-callout{ position:relative; margin:var(--sp-6) 0; padding:var(--sp-5) var(--sp-5) var(--sp-5) var(--sp-6);
  background:var(--mj-surface-2); border-left:6px solid var(--mj-red);
  border-radius:0 var(--radius-md) var(--radius-md) 0 }
.mj-callout__label{ display:flex; align-items:center; gap:8px; margin:0 0 var(--sp-2);
  font-family:var(--font-meta); font-size:.68rem; letter-spacing:1.8px; text-transform:uppercase;
  color:var(--mj-red); font-weight:700 }
.mj-callout p:last-child{ margin-bottom:0 }
.mj-callout--green{ border-left-color:var(--mj-growth); background:var(--mj-growth-subtle) }
.mj-callout--green .mj-callout__label{ color:#08694c }
.mj-callout--blue{ border-left-color:var(--mj-trust); background:var(--mj-trust-subtle) }
.mj-callout--blue .mj-callout__label{ color:var(--mj-trust) }
body.dark-mode .mj-callout{ background:var(--mj-surface-2) }
body.dark-mode .mj-callout--green{ background:rgba(16,185,129,.10) }
body.dark-mode .mj-callout--blue{ background:rgba(37,99,235,.10) }

/* --- Statement type (E6): big, urban, toned. The "BEST FOR GROWTH" voice. --- */
.mj-statement{ font-family:var(--font-display); font-weight:800; line-height:.92;
  letter-spacing:-.02em; text-transform:uppercase; overflow-wrap:break-word;
  font-size:clamp(1.9rem, 4.4vw, 3.1rem); color:var(--mj-red); opacity:.92; margin:0 }
.mj-statement--ink{ color:var(--mj-ink-900); opacity:1 }
.mj-statement--muted{ color:var(--mj-red); opacity:.34 }
.mj-statement__kicker{ display:flex; align-items:center; gap:9px; margin:0 0 var(--sp-3);
  font-family:var(--font-meta); font-size:.68rem; letter-spacing:2px; text-transform:uppercase;
  color:var(--mj-text-muted) }

/* --- Final verdict = an experience (E9). --- */
.mj-verdict-h{ position:relative; display:inline-block; padding-bottom:10px; margin-bottom:var(--sp-4) }
.mj-verdict-h::after{ content:""; position:absolute; left:0; bottom:0; width:100%; height:5px;
  background:var(--mj-red); border-radius:1px }
.mj-verdict-kicker{ display:flex; align-items:center; gap:9px; margin-bottom:var(--sp-3);
  font-family:var(--font-meta); font-size:.68rem; letter-spacing:2.4px; text-transform:uppercase;
  color:var(--mj-red); font-weight:700 }

/* --- CTA panel (E5/E7): brand bar + M + title, solid conversion body. --- */
.mj-cta{ margin:var(--sp-8) 0; border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  overflow:hidden; background:var(--mj-surface) }
.mj-cta__bar{ display:flex; align-items:center; gap:10px; padding:11px 18px;
  background:var(--mj-red); color:#fff;
  font-family:var(--font-display); font-weight:800; font-size:.78rem; letter-spacing:1px;
  text-transform:uppercase }
.mj-cta__bar .mj-attrib{ opacity:1; color:#fff }
.mj-cta__bar--ink{ background:var(--mj-ink-900) }
.mj-cta__body{ padding:var(--sp-6) var(--sp-5) }
/* the solid conversion result panel — never dull grey */
.mj-cta__result{ background:var(--mj-ink-900); color:#fff; padding:var(--sp-6) var(--sp-5);
  display:grid; grid-template-columns:1fr auto; gap:var(--sp-5); align-items:center }
.mj-cta__result--red{ background:var(--mj-red) }
.mj-cta__result--blue{ background:var(--mj-trust) }
.mj-cta__result h4{ color:#fff; margin:0 0 6px }
.mj-cta__result p{ color:rgba(255,255,255,.86); margin:0 }
.mj-cta__actions{ display:flex; gap:var(--sp-3); flex-wrap:wrap }
@media(max-width:640px){ .mj-cta__result{ grid-template-columns:1fr } }
/* ease / time / money-saved language wears the growth green */
.mj-ease{ color:var(--mj-growth-text); font-weight:700 }

/* ── FEE SCALE · `.mj-fscale` ─────────────────────────────────────────────
   Design authority, Opus 5, 2026-08-20. Owner observation 2.1.1.

       "An interactive fee scale insert that ends up showing how much difference
        customers will pay if they choose yoco, or ikhokha based on their
        transaction volume and for how long, in months, or years."

   Built ON `.mj-cta`, which had full CSS and no prototype — a bar, a body, a
   solid result panel and an action row is already a calculator. Only the parts
   that are genuinely new live here.

   THE BAR IS A COMPARISON, NOT A CHART. Two rows, each a name, a rate, a
   proportional bar and a rand figure. The bars share one scale so the gap is
   read at a glance rather than computed; the rand figures are there because a
   bar is a feeling and a number is a decision.

   THE RANGE INPUT IS THE FIRST ONE IN THIS LIBRARY. Nothing here had ever styled
   a form control, so the track, the thumb and the focus ring are defined once,
   for both engines, rather than left to the browser's own blue. */
.mj-fscale .mj-cta__bar{ justify-content:space-between; flex-wrap:wrap }
.mj-fscale__title{ font:inherit }
.mj-fscale__stamp{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:rgba(255,255,255,.82) }

.mj-fscale__lab{ display:flex; align-items:baseline; flex-wrap:wrap; gap:var(--sp-3);
  font-family:var(--font-meta); font-size:.64rem;
  letter-spacing:1.4px; text-transform:uppercase; font-weight:700;
  color:var(--mj-text-muted); margin-bottom:var(--sp-3) }

/* NOTHING ELSE ON THIS SITE MOVES, so a reader has no reason to expect this one
   does. The hint names the gesture once, quietly, in the same caption tier the
   library already uses for `__note` — sentence case against the label's caps, so
   it reads as a second voice rather than more label. Not a link colour: it does
   not navigate, and colouring instructions like links is the lie that taught
   readers to stop trusting underlines. */
.mj-fscale__hint{ font-family:var(--font-body); font-size:.78rem; font-weight:400;
  letter-spacing:0; text-transform:none; color:var(--mj-gray-400); line-height:1.4 }
.mj-fscale__hint::before{ content:"\2194\00a0"; color:var(--mj-red); font-weight:700 }
.mj-fscale__control{ display:flex; align-items:center; gap:var(--sp-4);
  margin-bottom:var(--sp-5) }
.mj-fscale__vol{ font-family:var(--font-display); font-weight:800; font-size:1.28rem;
  color:var(--mj-black); min-width:6.2ch; text-align:right; font-variant-numeric:tabular-nums }
body.dark-mode .mj-fscale__vol{ color:#fff }

/* the slider — track and thumb, both engines, no browser blue */
.mj-fscale__range{ flex:1 1 auto; -webkit-appearance:none; appearance:none;
  height:6px; border-radius:var(--radius-lg); background:var(--mj-gray-200); cursor:pointer;
  margin:0; min-width:0 }
.mj-fscale__range:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:4px }
/* THE HANDLE IS THE ONE PLACE THIS SYSTEM SOFTENS, AND ON PURPOSE.
   Design rule G4 forbids circles and the radius scale tops out at 3px, because
   every SURFACE here is square: cards, badges, chips, avatars. A slider handle is
   not a surface — it is the one thing on the page the reader physically grabs,
   and at 1px it read as a chip that had fallen on the track. Lucky: "the sharp
   edges makes it feel a bit less like a tool."

   5px on a 20px handle is a softened square, not a circle: still unmistakably
   part of this geometry, but shaped like something you can pick up. The exception
   is scoped to the handle alone — the track keeps the library's own radius. */
.mj-fscale__range::-webkit-slider-thumb{ -webkit-appearance:none; appearance:none;
  width:20px; height:20px; border-radius:5px; background:var(--mj-red);
  border:3px solid var(--mj-surface); box-shadow:0 1px 4px rgba(0,0,0,.28); cursor:grab }
.mj-fscale__range::-moz-range-thumb{ width:20px; height:20px; border-radius:5px;
  background:var(--mj-red); border:3px solid var(--mj-surface);
  box-shadow:0 1px 4px rgba(0,0,0,.28); cursor:grab }
.mj-fscale__range::-moz-range-track{ height:6px; border-radius:var(--radius-lg);
  background:var(--mj-gray-200) }

/* ── THE SCENARIO SWITCH ──────────────────────────────────────────────────
   AN ELEMENT THAT RECOMMENDS THE SAME BRAND AT EVERY INPUT IS A BANNER.

       "make sure that this element doesn't always favor only one company …
        if let's say the turnover is more than 50,000 and iKhokha then becomes
        cheaper, then … it should then switch to favor iKhokha."      — Lucky

   On the published SWIPE rate there is genuinely no crossover for this pair, and
   the article says so and sources it. But cost is not one number: the moment a
   reader needs their takings the same day, Yoco charges 1.50% for an Instant
   Payout and iKhokha's iK Debit Card is R0 — and the winner changes hands at
   about R7,150 a month. That is a real condition, from the same truth layer, and
   the reader is the only one who knows which side of it they are on.

   So the switch is not a gimmick: it is the second axis the comparison actually
   has. */
.mj-fscale__scenario{ display:grid; grid-template-columns:auto minmax(0,1fr);
  gap:4px var(--sp-3); align-items:center;
  padding:var(--sp-4); margin-bottom:var(--sp-5);
  background:var(--mj-surface-2); border-left:3px solid var(--mj-trust) }
body.dark-mode .mj-fscale__scenario{ background:#1c1c20 }
.mj-fscale__switch{ width:18px; height:18px; margin:0; accent-color:var(--mj-trust);
  cursor:pointer }
.mj-fscale__switchlab{ font-family:var(--font-display); font-weight:700; font-size:.9rem;
  color:var(--mj-black); cursor:pointer; line-height:1.3 }
body.dark-mode .mj-fscale__switchlab{ color:#fff }
.mj-fscale__scnote{ grid-column:2; font-size:.78rem; line-height:1.5;
  color:var(--mj-text-muted) }

/* the breakpoint, named out loud — a reader near it needs to know they are near it */
.mj-fscale__cross{ margin:var(--sp-4) 0 0; padding:10px var(--sp-4);
  background:var(--mj-growth-subtle); border-left:3px solid var(--mj-growth);
  font-size:.84rem; line-height:1.55; color:var(--mj-ink) }
body.dark-mode .mj-fscale__cross{ background:#12261f; color:#e7e7ea }
.mj-fscale__cross b{ font-family:var(--font-display) }
/* The sentence around the numbers is SLOTTED, not fixed — a card-machine
   comparison says "less a month", a hosting comparison will not, and this
   element is meant to travel between verticals. These carry no styling of their
   own; they exist so the words are addressable. */
.mj-fscale__crosswho, .mj-fscale__crossat{ font-family:var(--font-display) }
.mj-fscale__crosslead, .mj-fscale__crossnote,
.mj-fscale__vlead, .mj-fscale__vtail,
.mj-fscale__ylead, .mj-fscale__ytail{ font:inherit; color:inherit }
.mj-fscale__cross[hidden]{ display:none }

.mj-fscale__rows{ list-style:none; margin:0; padding:0;
  display:grid; gap:var(--sp-3) }
.mj-fscale__row{ display:grid; grid-template-columns:minmax(8.5rem,auto) minmax(0,1fr) auto;
  align-items:center; gap:var(--sp-4) }
.mj-fscale__who{ display:block; font-family:var(--font-display); font-weight:700;
  font-size:.94rem; color:var(--mj-black); line-height:1.25 }
body.dark-mode .mj-fscale__who{ color:#fff }
.mj-fscale__rate{ display:block; font-family:var(--font-meta); font-size:.58rem;
  letter-spacing:1px; text-transform:uppercase; font-weight:700;
  color:var(--mj-text-muted); margin-top:2px }
.mj-fscale__bar{ display:block; height:12px; border-radius:2px;
  background:var(--mj-gray-200); overflow:hidden }
.mj-fscale__fill{ display:block; height:100%; width:var(--fill,0%);
  background:var(--mj-ink-900); transition:width .18s ease }
/* the CHEAPER row is the one wearing the growth colour — the bar says which
   costs more, the colour says which to pick, and they must not contradict */
.mj-fscale__row.is-best .mj-fscale__fill{ background:var(--mj-growth) }
/* IN DARK MODE THE DEARER BAR WAS INK ON INK. The fill is --mj-ink-900, which
   is the point in light mode — the longest bar is the heaviest thing on the
   row. On a #0c0c0e page it disappeared, so the element showed one green bar
   and an empty track, and the comparison the whole insert exists to make was
   invisible. The winner keeps its colour; the loser inverts. */
body.dark-mode .mj-fscale__bar{ background:#2a2a2e }
body.dark-mode .mj-fscale__fill{ background:#8b8b93 }
body.dark-mode .mj-fscale__row.is-best .mj-fscale__fill{ background:var(--mj-growth) }
.mj-fscale__fee{ font-family:var(--font-display); font-weight:800; font-size:1.02rem;
  color:var(--mj-black); font-variant-numeric:tabular-nums; min-width:5.5ch;
  text-align:right }
body.dark-mode .mj-fscale__fee{ color:#fff }
.mj-fscale__row.is-best .mj-fscale__fee{ color:var(--mj-growth-text) }

.mj-fscale__basis{ margin:var(--sp-5) 0 0; font-size:.82rem; line-height:1.6;
  color:var(--mj-text-muted) }
.mj-fscale__verdict h4{ font-family:var(--font-display); font-size:1.02rem; line-height:1.35 }
.mj-fscale__verdict b,
.mj-fscale__winner, .mj-fscale__diff, .mj-fscale__year{ color:#fff }

@media(max-width:640px){
  .mj-fscale__row{ grid-template-columns:1fr auto; }
  .mj-fscale__bar{ grid-column:1 / -1; order:3 }
}

/* ── THE FITTED VARIANT · `.scms-rb--fitted` · observation 20.3 ──────────
       "design additional variations where the text and the element can be
        fitted WITHIN THE WIDTH OF THE CONTENT in that article, without any
        overflows … the text on the left side, and the review block to the right
        … I would say maybe PRIORITIZE THAT OVER THE SIDEBAR VERSION."  — Lucky

   The sidebar variant needs a column this site does not have on an article page,
   which is why it has never rendered on one. This one needs nothing: the block
   floats inside the reading measure and the paragraphs wrap around it, so the
   article stays exactly as wide as it was and nothing overflows.

   FLOAT, NOT GRID, AND THAT IS THE WHOLE POINT. A grid would put ONE paragraph
   beside the block and leave a hole under the shorter of the two. A float lets
   the prose run down the side and close underneath it — the reader keeps
   reading a single column of text that happens to have a card in it, which is
   what "a more fitted read" means.

   It clears itself: `flow-root` on the section stops a short float from hanging
   into whatever comes next, which is the classic way this arrangement breaks. */
.scms-rb--fitted{ float:right; width:min(46%, 300px); margin:4px 0 var(--sp-4) var(--sp-5);
  clear:right }
.scms-rb--fitted .rb-c-score{ margin-bottom:var(--sp-3) }
/* STACKED INSIDE A NARROW FLOAT. The compact variant is a three-column grid —
   score, body, action — which at 300px gives each column about 90px and wraps
   the verdict one word per line. Written at `.scms-rb--fitted.scms-rb--compact`
   it LOST the cascade to `.scms-rb.scms-rb--compact` (same specificity, earlier
   in the file after the block moved), so the grid stayed and the action panel
   hung out of the card. Three classes beats two, and it is stated rather than
   left to source order. */
.scms-rb.scms-rb--compact.scms-rb--fitted{ display:block }
.scms-rb--fitted .rb-c-score{ display:flex; align-items:center; gap:var(--sp-3) }
.scms-rb--fitted .rb-c-score .scms-rb-badge{ margin-top:0 !important }
.scms-rb--fitted .rb-c-action{ margin-top:var(--sp-4) }
.scms-rb--fitted .rb-c-fit{ display:grid; gap:4px }
.scms-rb--fitted .scms-rb-cta{ width:100%; text-align:center }
/* nothing inside may exceed the float's own box */
.scms-rb--fitted *{ max-width:100% }
/* below the reading measure a float leaves too little room for either side */
@media(max-width:640px){
  .scms-rb--fitted{ float:none; width:100%; margin-left:0 }
}

.scms-rb-pay{ display:grid; gap:1px; margin-bottom:8px }
.scms-rb-pay__was{ display:flex; align-items:baseline; gap:6px;
  font-family:var(--font-meta); font-size:.66rem; color:var(--mj-gray-400) }
.scms-rb-pay__was s{ font-size:.78rem }
.scms-rb-pay__now{ font-family:var(--font-display); font-weight:800;
  font-size:1.32rem; line-height:1.05; color:var(--mj-black) }
body.dark-mode .scms-rb-pay__now{ color:#fff }
.scms-rb-pay__src{ font-family:var(--font-meta); font-size:.58rem; letter-spacing:.8px;
  text-transform:uppercase; color:var(--mj-gray-400) }
/* Appearance comes from the shared deal-tag rule; only its placement in this
   grid is local. Four selectors, one design — see "THE DEAL TAG". */
.scms-rb-pay__tag{ justify-self:start; margin-top:4px }
body.dark-mode .scms-rb-pay__tag{ background:#12261f; color:#4EC98A }
/* THE SAME FACTS, LAID OUT WIDE. The full variant has room for a row, so the
   struck price, the reader's price and the saving sit beside each other just
   above the buttons — which is where 20.3 asks for them: "somewhere either
   before the buttons or next to the buttons … you do show that this is the
   final price you will pay from the price that's advertised on the website."
   Narrow variants stack the same three facts; the layout differs, the facts and
   their source do not. */
/* the facts row's key and value, addressable. They carried NO classes, so no
   slot could reach them and every review block on the site shipped the
   prototype's four figures — including an iKhokha block quoting Yoco's rate and
   Yoco's plan name. */
.rb-f-fk{ font:inherit; color:inherit }
.rb-f-fv{ font:inherit; color:inherit }

.scms-rb-pay--row{ display:flex; align-items:center; flex-wrap:wrap;
  gap:var(--sp-3) var(--sp-4); margin:0 0 var(--sp-4);
  padding:10px var(--sp-4); background:var(--mj-surface-2);
  border-left:3px solid var(--mj-growth) }
body.dark-mode .scms-rb-pay--row{ background:#1c1c20 }
.scms-rb-pay--row .scms-rb-pay__was{ flex-direction:column; align-items:flex-start; gap:0 }
.scms-rb-pay--row .scms-rb-pay__col{ display:grid }
.scms-rb-pay--row .scms-rb-pay__now{ font-size:1.18rem }
.scms-rb-pay__arrow{ color:var(--mj-gray-400); font-size:1rem }
.scms-rb-pay--row .scms-rb-pay__tag{ margin-top:0; margin-left:auto }

/* the plain price, when there is no verified offer to show against it */
.scms-rb-price{ font-family:var(--font-display); font-weight:800; font-size:1.25rem;
  color:var(--mj-black) }
body.dark-mode .scms-rb-price{ color:#fff }

/* --- Buy zone (E7): solid panel — image spans the left; offer text + action stack on
   the right so the offer always has room to breathe (no crushed middle column). --- */
.mj-buyzone{ display:grid; grid-template-columns:auto minmax(0,1fr);
  grid-template-areas:"img txt" "img act"; gap:9px var(--sp-5); align-items:center;
  background:var(--mj-ink-900); color:#fff; padding:var(--sp-5); border-radius:var(--radius-md);
  margin:var(--sp-5) 0 }
.mj-buyzone--red{ background:var(--mj-red) }
.mj-buyzone__img{ grid-area:img; align-self:center; width:88px; height:88px; background:#fff;
  border-radius:var(--radius-md); display:flex; align-items:center; justify-content:center; padding:8px }
.mj-buyzone__img img{ width:100%; height:100%; object-fit:contain }
.mj-buyzone__txt{ grid-area:txt; min-width:0; align-self:end }
.mj-buyzone__act{ grid-area:act; min-width:0; align-self:start }
.mj-buyzone__offer{ display:inline-flex; align-items:center; gap:7px;
  font-family:var(--font-meta); font-size:.66rem; letter-spacing:1.2px; text-transform:uppercase;
  color:#fff; opacity:.82 }
.mj-buyzone__deal{ font-family:var(--font-display); font-weight:800; font-size:1.06rem; color:#fff;
  line-height:1.2; margin:4px 0 0; overflow-wrap:break-word }
@media(max-width:720px){ .mj-buyzone{ grid-template-columns:1fr;
  grid-template-areas:"img" "txt" "act"; justify-items:start; text-align:left } }

/* --- Link system in full (W4/W5) — the DOTTED pattern, adopted from glossary-term.
   A dotted underline in the intent colour reads as an offer rather than a demand: it is
   present without shouting, and it resolves to a solid rule on hover so the link comes
   alive under the cursor. This replaced the old solid-underline + colour-fill-on-hover
   treatment, which felt intrusive in running prose. Weight carries the hierarchy instead
   of decoration: action 700, intelligence 600, structure 500. --- */
.mj-link{ text-decoration:none; border-bottom:1.5px dotted currentColor; padding-bottom:1px;
  transition:border-bottom-style .14s ease, border-bottom-color .14s ease, color .14s ease }
.mj-link:hover,.mj-link:focus-visible{ border-bottom-style:solid }
.mj-link:focus-visible{ outline:2px solid currentColor; outline-offset:3px; border-radius:1px }

/* ACTION — red. Affiliate / buy. Heaviest, because it costs the reader money. */
.mj-link--action{ color:var(--mj-red); font-weight:700 }
/* INTELLIGENCE — blue. Internal article or cited source. The everyday link. */
.mj-link--intel{ color:var(--mj-trust); font-weight:600 }
/* TRUST — green. Proof and verification. */
.mj-link--trust{ color:var(--mj-growth-text); font-weight:600 }
/* STRUCTURE — near-black. In-page navigation; lightest weight because it does not leave. */
.mj-link--structure{ color:var(--mj-ink-900); font-weight:500 }
body.dark-mode .mj-link--structure{ color:#e8e8ea }
.mj-link--faq{ color:var(--mj-trust); font-weight:800 }            /* last-chance surface: bold blue */

/* --- BYLINE + AUTHOR BLOCK — ONE canonical style (D1/D8/G10) -----------------
   REBUILT 2026-07-30. Previously the product-review template carried a red-left-bar
   panel while List used a quiet rule, so the same component looked like two components.
   There is now ONE treatment, taken from the List template because that is the approved
   one, and every article type uses it.

   Forbes/Verge discipline: SMALL and tight. Metadata labels are 10-11px, values are
   12-13px, the name is display-weight but not shouty, and the block is bounded by
   hairline rules rather than a coloured panel. It should read as a masthead credit,
   not as a card.

   ROLE LINE: never "Founder". Lucky is credited by his EDITORIAL function
   (Researcher / Fee-tested by / Reviewed by), because a founder byline invites the
   reader to discount the verdict as owner bias. The role describes the work done on
   THIS article, which is both more useful and more honest.
   -------------------------------------------------------------------------- */
/* THE BYLINE IS A CREDIT, NOT A CARD.
   Reduced one step throughout, 2026-08-15. It was carrying a display-weight name,
   an uppercase role, a credential line and a three-column meta block at sizes
   that made the block taller than the dek above it — so the first thing under
   the headline was administration rather than the article. Same structure, same
   information, same order; less height. */
.mj-avatar{ width:32px; height:32px; border-radius:2px; object-fit:cover; flex:0 0 auto;
  background:var(--mj-ink-900) }
.mj-avatar--lg{ width:56px; height:56px }

.mj-byline{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
  padding:var(--sp-3) 0; margin:var(--sp-4) 0;
  border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-byline{ border-color:#2a2a2d }
/* ── SHARE, HANGING BELOW THE BYLINE'S CLOSING RULE ────────────────────────
   Lucky, 2026-08-26: the share buttons should be "part of the author block ...
   underneath the bottom divider line". They were a loose sibling in the header,
   which left them stating no relationship to anything — a row of icons floating
   between an author and a headline.
   Full-width so they break to their own line, and pulled down past the byline's
   bottom border so they read as hanging off it rather than sitting inside the
   credit. `.mj-byline` is flex, so `flex:1 0 100%` is what "own row" means here. */
/* Lucky, 2026-08-26: "the bottom bar must not touch the share buttons".
   A negative bottom margin had pulled the row down onto the byline's closing
   rule, so the buttons sat ON the line rather than above it. Symmetrical
   padding instead — the row is separated from the meta above it and from the
   rule below it by the same gap, which is what makes it read as its own band. */
.mj-byline__share{ flex:1 0 100%; display:flex; align-items:center; gap:var(--sp-3);
  margin:0; padding:var(--sp-3) 0;
  border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-byline__share{ border-top-color:#2a2a2d }
.mj-byline__share > span{ font-family:var(--font-meta); font-size:.64rem;
  letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-gray-400) }
.mj-byline__sharelinks{ display:inline-flex; align-items:center; gap:8px }
.mj-byline__share a{ display:inline-flex; align-items:center; justify-content:center;
  width:30px; height:30px; border:1px solid var(--mj-rule); border-radius:var(--radius-sm);
  font-family:var(--font-meta); font-size:.7rem; color:var(--mj-gray-400);
  text-decoration:none; transition:.15s }
.mj-byline__share a:hover{ border-color:var(--mj-trust); color:var(--mj-trust);
  background:var(--mj-trust-subtle) }
body.dark-mode .mj-byline__share a{ border-color:#2a2a2d; color:#a8a8ae }
/* THE TWO HALVES MUST NOT PUSH EACH OTHER ONTO SEPARATE ROWS.
   `who` had no flex basis, so a long credential line made it wider than the
   column, `flex-wrap` sent the meta block to a second row, and the byline grew
   to 108px with a 57px step between the name and the dates. The credential
   should WRAP INSIDE its own half; the meta keeps its natural width and both
   stay on one line. Lucky: "there must always be a balance." */
/* THE FACE MUST CARRY THE STACK BESIDE IT.
   Measured: a 32px avatar against a 59px three-tier stack (name / role /
   credential). The square read as a stray thumbnail pinned to the top-left of a
   block it was too small to anchor, and the three tiers were 1px and 2px apart —
   glued into one dense paragraph of mixed type. Lucky: "it feels out of balance."
   46px reads as the anchor of the block at both credential lengths (1 line → 51px
   stack, 2 lines → 65px), and the tiers now have air proportional to their size. */
.mj-byline__who{ display:flex; align-items:center; gap:var(--sp-3); min-width:0;
  flex:1 1 240px }
.mj-byline__who > .mj-avatar{ width:46px; height:46px }
.mj-byline__who > span{ min-width:0 }
.mj-byline__name{ display:block; font-family:var(--font-display); font-weight:700;
  font-size:.82rem; line-height:1.2; color:var(--mj-black); letter-spacing:-.005em }
body.dark-mode .mj-byline__name{ color:#fff }
.mj-byline__name a{ color:inherit; text-decoration:none;
  border-bottom:1.5px dotted transparent; transition:border-color .14s ease, color .14s ease }
.mj-byline__name a:hover{ color:var(--mj-trust); border-bottom-color:var(--mj-trust) }
/* the role — bold, small, and it describes the WORK, never the job title */
.mj-byline__role{ display:block; font-family:var(--font-meta); font-size:.56rem;
  letter-spacing:.8px; text-transform:uppercase; font-weight:700; color:var(--mj-text-muted);
  margin-top:4px; line-height:1.25 }
/* metadata: label above value, both small. Forbes sets these ~10/12px. */
.mj-byline__meta{ display:flex; align-items:center; gap:var(--sp-4); margin-left:auto;
  flex:0 0 auto }
.mj-byline__sep{ width:1px; height:22px; background:var(--mj-rule); flex:0 0 auto }
body.dark-mode .mj-byline__sep{ background:#2a2a2d }
/* OBSERVATION 10.1 — "add subtle colour to the published, verified or read
   metadata … make the section feel less dead."

   The labels sat at `--mj-gray-400`: 3.18:1 on paper, below the 4.5 this site
   holds everything else to, which is most of why the block read as dead rather
   than quiet. `--mj-text-muted` is 6.50:1 at the same size and weight.

   COLOUR GOES ON THE PROOF ONLY. Giving all three metrics a tint would flatten
   the one distinction that matters: published and read are facts about the
   article, fee-verified is a promise about the numbers. It gets the growth
   colour and a tick; the other two stay deliberately plain, which is what makes
   the green mean something. */
.mj-byline__mi{ display:block; font-family:var(--font-meta); font-size:.53rem;
  letter-spacing:1px; text-transform:uppercase; color:var(--mj-text-muted); font-weight:700;
  line-height:1.25 }
.mj-byline__mi--verified{ color:var(--mj-growth-text) }
.mj-byline__mi--verified::before{ content:"\2713"; margin-right:4px; font-weight:800 }
.mj-byline__mi b{ display:block; font-family:var(--font-display); font-size:.7rem;
  letter-spacing:0; text-transform:none; color:var(--mj-black); font-weight:700; margin-top:1px }
body.dark-mode .mj-byline__mi b{ color:#fff }
/* a second credit (reviewer / fee-checker) sits on the SAME line rhythm, tight to the first */
/* it may run to a second line inside its own half — that is the balance */
.mj-byline__second{ display:block; font-family:var(--font-meta); font-size:.54rem;
  letter-spacing:.6px; text-transform:uppercase; color:var(--mj-gray-400); font-weight:700;
  margin-top:5px; line-height:1.35 }
.mj-byline__second a{ color:var(--mj-trust); text-decoration:none;
  border-bottom:1.5px dotted var(--mj-trust) }
.mj-byline__second a:hover{ border-bottom-style:solid }
/* multi-author: two credits side by side, still one row */
.mj-byline--multi .mj-byline__who + .mj-byline__who{ padding-left:var(--sp-4);
  border-left:1px solid var(--mj-rule) }
body.dark-mode .mj-byline--multi .mj-byline__who + .mj-byline__who{ border-left-color:#2a2a2d }
@media(max-width:680px){
  .mj-byline__meta{ margin-left:0; width:100%; gap:var(--sp-4) }
  .mj-byline__sep{ display:none }
  .mj-byline--multi .mj-byline__who + .mj-byline__who{ padding-left:0; border-left:0 }
}

/* the FULL author box — foot of trust/education articles only (D8), never on a review */
.mj-authorbox{ display:flex; gap:var(--sp-4); align-items:flex-start;
  padding:var(--sp-5) 0; margin:var(--sp-7) 0;
  border-top:2px solid var(--mj-black); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-authorbox{ border-top-color:#fff; border-bottom-color:#2a2a2d }
.mj-authorbox h4{ margin:0 0 3px; font-family:var(--font-display); font-size:.98rem;
  font-weight:700 }
.mj-authorbox p{ margin:0; font-size:.88rem; color:var(--mj-text-muted); line-height:1.6 }

/* --- Highlight utilities (W3). Strategic emphasis only. --- */
/* THE OTHER STATE OF THE CROSSOVER LINE. `.mj-fscale__cross` says where the
   answer changes hands; this says why it never does. A scale that names one
   winner at every input is the shape of a paid placement, so on the rare
   occasion it is simply TRUE, the element has to volunteer the reason in the
   same place the crossover would have appeared — quieter than the verdict,
   louder than the basis note. */
.mj-fscale__nocross{ margin:10px 0 0; font-family:var(--font-meta); font-size:.78rem;
  line-height:1.5; color:var(--mj-gray-400) }
.mj-fscale__nocross[hidden]{ display:none }
.mj-fscale__nocrosslead{ font-weight:700; color:var(--mj-charcoal); margin-right:4px }
.mj-fscale__nocrossreason{ color:var(--mj-gray-400) }
body.dark-mode .mj-fscale__nocrosslead{ color:#e9e9ec }
.mj-hl{ font-weight:700; color:var(--mj-ink) }
/* LINING FIGURES ON THE NUMBERS A READER SCANS.
   R3 says an mj-hl span IS the deciding number, so these carry prices more
   often than they carry words. The body face falls back to Georgia today, and
   Georgia sets OLD-STYLE figures: a lone zero sits at x-height, so "R0" renders
   as "Ro" — a selling point of this whole vertical, silently turned into a
   typo. This declaration is inert against Georgia (it has no lining set) and
   correct the moment the real body font loads. The visible fix is the missing
   font files, not this rule; this rule is what stops it coming back. */
.mj-hl, .mj-hl--red, .mj-hl--green, .mj-hl--blue,
.mj-ed-sec p > strong, .mj-ed-sec p > b{ font-variant-numeric:lining-nums }
.mj-hl--red{ color:var(--mj-red); font-weight:800 }
.mj-hl--green{ color:var(--mj-growth-text); font-weight:800 }
.mj-hl--blue{ color:var(--mj-trust); font-weight:800 }
.mj-hl--mark{ background:linear-gradient(transparent 62%, var(--mj-growth-subtle) 62%); font-weight:700 }
body.dark-mode .mj-hl--mark{ background:linear-gradient(transparent 62%, rgba(16,185,129,.26) 62%) }

/* --- Sticky slim article header + companion nav (C1/C2/C3). --- */
.mj-readbar{ position:fixed; top:0; left:0; right:0; z-index:9500;
  background:var(--mj-ink-900); color:#fff;
  transform:translateY(-102%); transition:transform .28s cubic-bezier(.4,0,.2,1) }
.mj-readbar.is-on{ transform:translateY(0) }
.mj-readbar__in{ max-width:1400px; margin:0 auto; padding:0 var(--sp-5); height:52px;
  display:flex; align-items:center; gap:var(--sp-4) }
.mj-readbar__mark{ width:24px; height:24px; border-radius:1px; flex:0 0 auto;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28.92 28.92'%3E%3Crect width='28.92' height='28.92' fill='%23ec2227'/%3E%3Cpolygon points='21.8 5.28 21.64 5.28 17.9 13.31 17.9 13.35 17.88 13.35 13.92 21.84 13.06 23.67 16.99 23.67 17.84 21.84 17.9 21.84 17.9 21.73 21.8 13.35 21.8 23.67 25.36 23.67 25.36 13.35 25.36 5.72 25.36 5.28 21.8 5.28' fill='%23fff'/%3E%3Cpath d='M18.29,5.25h-4.11l-.46.79-4.2,7.28-5.96,10.32h4.11c2.02-3.49,4.03-6.98,6.05-10.48v5.99l2.72-5.83,3.76-8.07h-1.91Z' fill='%23fff'/%3E%3C/svg%3E");
  background-size:contain; background-repeat:no-repeat }
.mj-readbar__meta{ min-width:0; display:flex; flex-direction:column; justify-content:center }
.mj-readbar__eyebrow{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.8px;
  text-transform:uppercase; color:rgba(255,255,255,.5); line-height:1 }
.mj-readbar__title{ font-family:var(--font-display); font-weight:700; font-size:.86rem;
  color:#fff; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:1.35 }
/* companion nav docks with the bar as ONE unit */
.mj-readbar__nav{ margin-left:auto; display:flex; gap:var(--sp-4); align-items:center;
  font-family:var(--font-display); font-weight:600; font-size:.72rem; letter-spacing:.5px;
  text-transform:uppercase; overflow-x:auto; scrollbar-width:none }
.mj-readbar__nav::-webkit-scrollbar{ display:none }
.mj-readbar__nav a{ color:rgba(255,255,255,.72); text-decoration:none; white-space:nowrap;
  padding:4px 0; border-bottom:2px solid transparent; transition:.15s }
.mj-readbar__nav a:hover, .mj-readbar__nav a.is-here{ color:#fff; border-bottom-color:var(--mj-red) }
.mj-readbar__prog{ position:absolute; left:0; bottom:0; height:2px; background:var(--mj-red); width:0 }
/* C3 — content never crowds the bar */
body.readbar-on{ scroll-padding-top:calc(52px + var(--sp-6)) }
/* EVERY ANCHOR ON THE SITE MUST CLEAR THE PINNED HEADER. The table of contents
   and the navblock both jump to headings, and a sticky header eats whatever it
   lands on — so the reader clicked "Fees" and arrived at the paragraph AFTER
   the "Fees" heading, with no way to tell the jump had misfired.
   `scroll-padding-top` belongs on the SCROLLING element, which is <html>; the
   readbar rule above sets it on <body>, where it is inherited rather than
   applied and only holds because nothing overrides it. */
html{ scroll-padding-top:calc(var(--mj-header-top) + var(--sp-4)) }
@media(max-width:860px){ .mj-readbar__nav{ display:none } }

/* ═══ LAUNCH ELEMENTS — renderer output (build.py). Square-first, token-driven. ═══ */

/* Product box: no drawn frame; alignment + rules ARE the border (directives v2 E6) */
.mj-pbx{ display:grid; grid-template-columns:.9fr 1.1fr; gap:var(--sp-6);
  border-top:2px solid var(--mj-ink-900); border-bottom:2px solid var(--mj-ink-900);
  padding:var(--sp-6) 0; margin:var(--sp-7) 0 }
/* min-width:0 stops the big statement's min-content from stealing the main column's
   width (which was crushing the buy-zone offer into a sliver). */
.mj-pbx > *{ min-width:0 }
.mj-pbx__stmt{ display:flex; flex-direction:column; justify-content:flex-end }
.mj-pbx__main h3{ font-size:1.55rem; margin:0 0 8px }
.mj-pbx__hook{ color:var(--mj-text-muted); margin:0 0 var(--sp-4); line-height:1.6 }
.mj-pbx__price{ font-family:var(--font-display); font-weight:800; font-size:2.3rem; line-height:1;
  color:var(--mj-black); letter-spacing:-.02em; margin:0 0 var(--sp-4) }
.mj-pbx__price .was{ font-size:.42em; color:var(--mj-gray-400); text-decoration:line-through;
  font-weight:500; margin-left:9px }
.mj-pbx__price small{ display:block; font-family:var(--font-meta); font-size:.3em; font-weight:400;
  letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-gray-400); margin-top:9px }
.mj-pbx__specs{ list-style:none; margin:0 0 var(--sp-5); padding:0 }
.mj-pbx__specs li{ display:flex; justify-content:space-between; gap:16px; padding:10px 0;
  border-bottom:1px solid var(--mj-rule); font-size:.93rem }
.mj-pbx__specs .k{ color:var(--mj-text-muted) } .mj-pbx__specs .v{ font-weight:700; text-align:right }
/* in-body affiliate disclosure: small, quiet, italic — a footnote, never an offer (§9) */
.mj-pbx__disc{ font-family:var(--font-meta); font-size:.6rem; font-style:italic; letter-spacing:.2px;
  color:var(--mj-gray-400); opacity:.85; margin:10px 0 0; line-height:1.5 }
@media(max-width:720px){ .mj-pbx{ grid-template-columns:1fr } }

/* Review block */
.scms-rb{ border:1px solid var(--mj-rule); border-radius:var(--radius-md); overflow:hidden;
  margin:var(--sp-7) 0; background:var(--mj-surface) }
.scms-rb__top{ display:flex; align-items:center; gap:9px; padding:11px 18px; background:var(--mj-ink-900);
  color:#fff; font-family:var(--font-display); font-weight:800; font-size:.74rem; letter-spacing:1px;
  text-transform:uppercase }
.scms-rb__top .mj-attrib{ color:#fff }
.scms-rb__body{ display:flex; gap:var(--sp-5); align-items:center; padding:var(--sp-5) }
.scms-rb__score{ flex:0 0 auto; width:82px; height:82px; border-radius:var(--radius-md);
  background:var(--mj-growth); color:#fff; display:flex; flex-direction:column; align-items:center;
  justify-content:center; font-family:var(--font-display) }
.scms-rb__score b{ font-size:1.9rem; line-height:1; font-weight:800 }
.scms-rb__score small{ font-size:.58rem; letter-spacing:1px; text-transform:uppercase; opacity:.9 }
.scms-rb__lead h3{ margin:0 0 6px; font-size:1.2rem }
.scms-rb__lead p{ margin:0; color:var(--mj-text-muted); line-height:1.6 }
.scms-rb__bars{ list-style:none; margin:0; padding:0 var(--sp-5) var(--sp-4) }
.scms-rb__bars li{ display:grid; grid-template-columns:120px 1fr 38px; gap:12px; align-items:center;
  padding:5px 0; font-size:.88rem }
.scms-rb__bars .k{ color:var(--mj-text-muted) }
.scms-rb__bars .bar{ background:var(--mj-surface-2); height:7px; border-radius:1px; overflow:hidden }
.scms-rb__bars .bar i{ display:block; height:100%; background:var(--mj-growth) }
.scms-rb__bars .v{ font-weight:700; text-align:right }
.scms-rb__split{ display:grid; grid-template-columns:1fr 1fr; gap:1px; background:var(--mj-rule);
  border-top:1px solid var(--mj-rule) }
.scms-rb__split p{ background:var(--mj-surface); margin:0; padding:var(--sp-4) var(--sp-5);
  font-size:.9rem; line-height:1.55 }
.scms-rb__split .best b{ color:#08694c } .scms-rb__split .avoid b{ color:var(--mj-red) }
.scms-rb .mj-buyzone{ margin:0; border-radius:0 }
@media(max-width:640px){ .scms-rb__split{ grid-template-columns:1fr } .scms-rb__bars li{ grid-template-columns:96px 1fr 34px } }

/* ═══ Architect-added inserts (build-plan §3) ═══════════════════════════════ */
/* pull-stat / big-number — one striking figure */
.mj-pullstat{ margin:var(--sp-7) 0; padding:var(--sp-5) 0; border-top:2px solid var(--mj-ink-900);
  border-bottom:2px solid var(--mj-ink-900); display:flex; align-items:baseline; gap:var(--sp-5);
  flex-wrap:wrap }
.mj-pullstat__n{ font-family:var(--font-display); font-weight:800; letter-spacing:-.02em;
  font-size:clamp(2.6rem,7vw,4.6rem); line-height:.9; color:var(--mj-red) }
.mj-pullstat figcaption{ font-size:1.02rem; color:var(--mj-text-muted); max-width:34ch; line-height:1.5 }
/* at-a-glance verdict strip — the short answer, under the hero */
/* ── THE RED RULE BELONGS TO THE ANSWER, NOT TO WHAT FOLLOWS IT ───────────
   Lucky: "the red border can end up where we cover the short answer paragraph…
   and then there should be freedom to fit in more inserts, but those inserts
   should be in the white background."

   He is describing a real distinction. The rule is the strip's identity for the
   SENTENCE — it is what says "this is the answer". Carried on down past the
   elements it becomes a frame around a frame, and worse, it caps their width:
   three product boxes inside a 680px column with padding on both sides get
   crushed, and the one place on the page where a reader is deciding is the one
   place with no room.

   So the strip stops painting itself at the end of the answer. `--sa-pad` is
   the padding the answer uses; the panel region below cancels it and takes the
   full column, and an element inside may break wider still. */
.mj-vstrip{ --sa-pad:var(--sp-5);
  margin:var(--sp-5) 0 var(--sp-7);
  background:var(--mj-surface-2) }
/* The tinted, red-ruled region is the answer itself — kicker, sentence,
   qualifier — and nothing after it. */
.mj-vstrip__k,
.mj-vstrip > p{ border-left:4px solid var(--mj-red);
  padding-left:calc(var(--sa-pad) - 4px); padding-right:var(--sa-pad) }
.mj-vstrip__k{ padding-top:var(--sp-4) }
.mj-vstrip > p:last-child{ padding-bottom:var(--sp-4) }
/* THE RULE IS ONE RULE. The kicker and the sentence each draw their own
   border-left, and the kicker carried `margin-bottom:5px` — so the two borders
   were five pixels apart and the strip's identity read as a dashed line with
   one gap in it. Margin between bordered siblings breaks the border; padding
   inside them does not. */
.mj-vstrip__k{ display:flex; align-items:center; gap:8px; font-family:var(--font-meta);
  font-size:.64rem; letter-spacing:1.6px; text-transform:uppercase; font-weight:700;
  color:var(--mj-gray-400); margin-bottom:0; padding-bottom:5px }
.mj-vstrip > p{ margin:0; font-family:var(--font-display); font-weight:700; font-size:1.16rem;
  line-height:1.4; color:var(--mj-black) }
/* inline offer chip — small green affiliate chip inside prose */
.mj-chip{ display:inline-flex; align-items:center; gap:7px; vertical-align:baseline;
  background:var(--mj-growth); color:#fff; padding:3px 11px; border-radius:2px; text-decoration:none;
  font-family:var(--font-display); font-weight:700; font-size:.86rem; line-height:1.5;
  transition:background .15s ease }
.mj-chip .mj-sq{ background:#fff }
.mj-chip:hover{ background:#0a7a52 }
/* trust bar — authority strip near the top */
.mj-trustbar{ list-style:none; margin:var(--sp-5) 0; padding:12px var(--sp-5); display:flex;
  flex-wrap:wrap; gap:8px var(--sp-6); background:var(--mj-ink-900); border-radius:var(--radius-md) }
.mj-trustbar li{ display:flex; align-items:center; gap:8px; margin:0; color:#fff;
  font-family:var(--font-meta); font-size:.74rem; letter-spacing:.4px }
/* related-reading rail — cross-links at the foot */
/* KEEP READING (J-7b, 2026-10-04): the newsroom's picture cards on a soft band, three across; two across on a
   phone so six stories stay one short scroll, not six screens. */
.mj-related{ margin:var(--sp-8) 0 var(--sp-6); padding:24px 24px 26px; background:#F7F7F5; border-radius:var(--radius-md);
  border-top:3px solid var(--mj-ink-900) }
body.dark-mode .mj-related{ background:#17171b; border-top-color:#f4f4f5 }
.mj-related__head{ display:flex; flex-wrap:wrap; align-items:baseline; justify-content:space-between; gap:6px 12px; margin:0 0 18px }
.mj-related__h{ margin:0; font-family:var(--font-display); font-weight:800; font-size:1.3rem; letter-spacing:-.01em; color:var(--mj-black); white-space:nowrap }
body.dark-mode .mj-related__h{ color:#fff }
.mj-related__all{ font-family:var(--font-meta); font-size:.66rem; font-weight:700; letter-spacing:1px; text-transform:uppercase;
  color:var(--mj-ink); text-decoration:none; white-space:nowrap }
.mj-related__all::after{ content:" \203A" }
.mj-related__all:hover{ color:var(--mj-red) }
body.dark-mode .mj-related__all{ color:#e7e7ea }
.mj-related__grid{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:22px 20px }
.mj-related .mj-nx-card a{ color:inherit; text-decoration:none }
.mj-related .mj-nx-pic{ aspect-ratio:16/10; border-radius:var(--radius-sm) }
.mj-related .mj-nx-t{ font-size:1rem; line-height:1.25 }
.mj-related .mj-nx-card:hover .mj-nx-t a{ color:var(--mj-red) }
@media(max-width:640px){
  .mj-related{ padding:18px 16px 20px }
  .mj-related__h{ font-size:1.12rem }
  .mj-related__all{ white-space:normal }
  .mj-related__grid{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px 12px }
  .mj-related .mj-nx-t{ font-size:.9rem }
}

/* ═══ Insert-library showcase page chrome ══════════════════════════════════ */
.lib-item{ margin:0 0 var(--sp-9) }
.lib-item__head{ margin:0 0 var(--sp-4); padding-bottom:10px; border-bottom:2px solid var(--mj-ink-900) }
.lib-item__head h2{ margin:0 0 4px; font-size:1.5rem }
.lib-item__meta{ font-family:var(--font-meta); font-size:.78rem; color:var(--mj-text-muted);
  line-height:1.55; margin:0 }
.lib-item__meta b{ color:var(--mj-black); font-family:var(--font-display) }
.lib-var{ margin:var(--sp-5) 0 }
.lib-var__label{ display:inline-flex; align-items:center; gap:7px; margin:0 0 10px;
  font-family:var(--font-meta); font-size:.64rem; letter-spacing:1.4px; text-transform:uppercase;
  font-weight:700; color:var(--mj-gray-400) }
.lib-toc{ position:sticky; top:90px; align-self:start }
.lib-toc a{ display:block; padding:5px 0; font-size:.9rem; color:var(--mj-text-muted);
  text-decoration:none }
.lib-toc a:hover{ color:var(--mj-red) }

/* Figures / graphics — registered slots, never faked */
.mj-fig{ margin:var(--sp-6) 0 }
.mj-fig__frame{ background:var(--mj-surface-2); border:1px dashed var(--mj-gray-300);
  border-radius:var(--radius-md); min-height:180px; display:flex; align-items:center; justify-content:center }
.mj-fig__pending{ display:flex; align-items:center; gap:8px; font-family:var(--font-meta);
  font-size:.72rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-gray-400) }
.mj-fig figcaption{ display:flex; align-items:center; gap:10px; margin-top:9px;
  font-family:var(--font-meta); font-size:.72rem; color:var(--mj-gray-400) }

/* Navblock + table hint */
.mj-navblock{ border-left:5px solid var(--mj-red); background:var(--mj-surface-2);
  padding:var(--sp-4) var(--sp-5); margin:var(--sp-6) 0; border-radius:0 var(--radius-md) var(--radius-md) 0 }
.mj-tbl-hint{ font-family:var(--font-meta); font-size:.7rem; color:var(--mj-gray-400); margin:8px 0 0 }

/* ═══ INSERT VARIATIONS — one insert, many idealised use cases (SmartCMS conditional logic) ═══ */
/* product-box · SLIM — an in-body mention that must not break reading flow */
.mj-pb--slim{ display:grid; grid-template-columns:58px 1fr auto; gap:var(--sp-4); align-items:center;
  padding:var(--sp-4) var(--sp-5); margin:var(--sp-5) 0; background:var(--mj-surface);
  border:1px solid var(--mj-rule); border-left:4px solid var(--mj-red); border-radius:0 var(--radius-md) var(--radius-md) 0 }
/* A SLIM ROW WITHOUT A THUMBNAIL MUST NOT KEEP THE THUMBNAIL'S COLUMN.
   The template makes the image cell conditional — `{{#if image}}` — but this
   grid always declared three tracks and sized the first for a 58px thumb. With
   no image every child shifted one column left: the BODY landed in the thumb
   track and wrapped itself into a 64px ribbon 281px tall, and the price and
   action landed where the body belonged. That is what the reveal panel in the
   card-machines ranking was showing.
   The structure is the fact here, so the stylesheet reads the structure rather
   than waiting for a renderer to label it — one rule, and every slim row on the
   site is fixed at once, including the ones nobody has looked at yet. */
.mj-pb--slim:not(:has(> .mj-pb__img)):not(:has(> .mj-pb--slim__img)){
  grid-template-columns:minmax(0,1fr) auto }
.mj-pb--slim__img{ width:58px; height:58px; background:#fff; border-radius:var(--radius-sm);
  display:flex; align-items:center; justify-content:center; padding:5px }
.mj-pb--slim__img img{ width:100%; height:100%; object-fit:contain }
.mj-pb--slim h4{ margin:0; font-size:1rem } .mj-pb--slim p{ margin:2px 0 0; font-size:.86rem; color:var(--mj-text-muted) }
.mj-pb--slim__right{ display:flex; align-items:center; gap:var(--sp-4) }
.mj-pb--slim .price{ font-family:var(--font-display); font-weight:800; font-size:1.2rem; white-space:nowrap }
@media(max-width:620px){ .mj-pb--slim{ grid-template-columns:1fr } .mj-pb--slim__right{ justify-content:space-between } }

/* product-box · STACKED — rail / grid card */
.mj-pb--stacked{ display:flex; flex-direction:column; gap:10px; padding:var(--sp-5);
  border:1px solid var(--mj-rule); border-radius:var(--radius-md); background:var(--mj-surface) }
.mj-pb--stacked__img{ aspect-ratio:4/3; background:#fff; border-radius:var(--radius-sm);
  display:flex; align-items:center; justify-content:center; padding:10px }
.mj-pb--stacked__img img{ width:100%; height:100%; object-fit:contain }
.mj-pb--stacked h4{ margin:0; font-size:1.05rem }
.mj-pb--stacked .price{ font-family:var(--font-display); font-weight:800; font-size:1.3rem; margin:0 }
.mj-pb--stacked .bf{ font-size:.86rem; color:var(--mj-text-muted); margin:0 }

/* review-block · COMPACT (quick verdict) — score + one line, mid-article */
.scms-rb--compact{ display:grid; grid-template-columns:auto 1fr auto; gap:var(--sp-4); align-items:center;
  padding:var(--sp-4) var(--sp-5); margin:var(--sp-5) 0; background:var(--mj-surface-2);
  border-left:4px solid var(--mj-growth); border-radius:0 var(--radius-md) var(--radius-md) 0 }
.scms-rb--compact .sc{ width:52px; height:52px; background:var(--mj-growth); color:#fff;
  border-radius:var(--radius-sm); display:flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; font-size:1.25rem }
.scms-rb--compact .nm{ font-family:var(--font-display); font-weight:800; margin:0; font-size:1rem }
.scms-rb--compact .vd{ margin:2px 0 0; font-size:.88rem; color:var(--mj-text-muted); line-height:1.5 }
@media(max-width:620px){ .scms-rb--compact{ grid-template-columns:auto 1fr } }

/* review-block · SIDEBAR — sticky rail card */
.scms-rb--side{ border:1px solid var(--mj-rule); border-radius:var(--radius-md); padding:var(--sp-5);
  background:var(--mj-surface); display:flex; flex-direction:column; gap:10px }
.scms-rb--side .sc{ align-self:flex-start; background:var(--mj-growth); color:#fff; padding:6px 12px;
  border-radius:var(--radius-sm); font-family:var(--font-display); font-weight:800; font-size:1.15rem }
.scms-rb--side h4{ margin:0; font-size:1.02rem }
.scms-rb--side .price{ font-family:var(--font-display); font-weight:800; font-size:1.2rem; margin:0 }

/* narrative tones — the messaging emphasis, applied consistently across inserts */
.mj-tone--verdict{ border-left-color:var(--mj-red) }
.mj-tone--price{ border-left-color:var(--mj-ink-900) }
.mj-tone--deal{ border-left-color:var(--mj-growth) }
.mj-tone--compare{ border-left-color:var(--mj-trust) }
.mj-tone--trust{ border-left-color:var(--mj-growth) }

/* =============================================================================
   L0 — PROMOTED FROM PROTOTYPE INLINE <style> BLOCKS  (2026-07-30, Opus 5)
   Directive v3 L0: element CSS lives in the library, never in a prototype page.
   138 classes were trapped inline and could not render in Elementor or dist/.
   Prototype vocabulary is canonical (it drives Elementor). Where it collided with
   the live launch renderer, the prototype rule is scoped by its root co-class so
   launch/dist/ renders exactly as before.
   ============================================================================= */

/* --- Tokens the library referenced but never defined (L0 fix).
   `--sp-*` was used 56 times in this file and existed only inside
   mashjoy-site-chrome.html, so every rule using it silently collapsed anywhere
   that page was not loaded — including launch/dist/ and Elementor. --- */
:root{
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px;
  --sp-6:32px; --sp-7:48px; --sp-8:64px; --sp-9:96px;
  --wide:960px;                 /* break-out width for inserts wider than the reading measure */
  --chrome-max:1400px;          /* header/footer max width */
  --mj-util-h:33px;             /* the utility bar: 32px of content + its 1px rule */
  --mj-header-top:73px;         /* WHAT STAYS PINNED once you scroll: the nav bar only
                                   (72px + its 1px rule). Anything that must clear the
                                   pinned chrome — anchor targets, the companion rail —
                                   measures itself against THIS, so it has to mean the
                                   pinned height and nothing else. It previously held 38
                                   (the utility bar) and was read as the pinned height in
                                   two places, which is how one number came to be wrong
                                   in both directions at once. */
  --font-serif:Georgia,'Times New Roman',serif;
  --mj-gray-100:#f4f4f4;
}

/* --- Article chrome — header, utility bar, drawer, companion nav, progress
     (from mashjoy-article--list.html) --- */
/* THE STICKY HEADER PINS FLUSH. The utility bar rides ABOVE the fold line and
   scrolls away; the nav bar lands at 0.

   It used to say `top:38px`, which pinned the WHOLE header — utility bar
   included — 38px down the viewport. The utility bar had already scrolled out
   of the way by then, so what those 38px held was the page background: a strip
   of nothing above a header that was supposedly stuck to the top.

   A sticky element cannot pin a PART of itself, and the utility bar is inside
   this header. So the header pins by its OWN top edge and we pull that edge up
   by exactly the utility bar's height — the bar leaves the screen at the moment
   the nav bar reaches 0, and the two motions read as one. */
.mj-header{position:sticky;top:calc(-1 * var(--mj-util-h));z-index:9000;width:100%;
  background:var(--mj-surface);
  border-bottom:1px solid var(--mj-gray-200);transition:background .3s,border-color .3s}
/* No utility bar to hide → nothing to pull up. Without this the contained
   surfaces (Product Review, Comparison, FAQ, Deals, product, legal) would pin
   33px too high and lose the top of their own nav bar off-screen — the same
   defect, inverted. */
body:not(.topbar-on) .mj-header{top:0}
body.dark-mode .mj-header{background:#101012;border-color:#2a2a2a;color:#fff}
/* ONE HAIRLINE UNDER THE HEADER, NOT TWO (2026-09-12). The utility strip and the
   header each carried a bottom border, so every page opened with two rules a few
   pixels apart. Lucky: "fix an issue where we have two borders." The header keeps
   its own, because that is the one that survives the strip scrolling away under
   the sticky bar. */
.mj-util{border-bottom:0;background:var(--mj-surface)}
body.dark-mode .mj-util{background:#0b0b0c;border-color:#2a2a2a}
.mj-util-inner{max-width:var(--chrome-max);margin:0 auto;padding:0 var(--sp-5);height:32px;
  display:flex;align-items:center;justify-content:space-between;
  font-family:var(--font-meta);font-size:.68rem;letter-spacing:1.5px;text-transform:uppercase;color:var(--mj-gray-400)}
.mj-util a{color:var(--mj-gray-400);text-decoration:none}
.mj-util a:hover{color:var(--mj-red)}
.mj-util .util-right{display:flex;gap:var(--sp-5)}
.mj-bar{max-width:var(--chrome-max);margin:0 auto;padding:0 var(--sp-5);height:72px;
  display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--sp-5)}
.mj-left{display:flex;align-items:center;gap:var(--sp-4)}
.mj-burger{display:none;background:none;border:none;cursor:pointer;padding:6px;color:inherit}
.mj-burger svg{width:26px;height:26px;stroke:currentColor;stroke-width:2;fill:none}
.mj-logo{display:flex;align-items:center;text-decoration:none;line-height:1}
.mj-logo img{height:26px}
.mj-logo .logo-dark{display:none}
body.dark-mode .mj-logo .logo-light{display:none}
body.dark-mode .mj-logo .logo-dark{display:block}
/* The conversion-containment panel is HIDDEN by default and revealed only by
   `body.nav-contain` (see below). Its base was `display:flex`, i.e. always on —
   which is why the built explainer showed "IN THIS COMPARISON / YOCO / IKHOKHA".
   Same class of defect as `.mm`: the state that hides an element was never in
   the library, so the element could not be off. */
.mj-contain{display:none;align-items:center;gap:var(--sp-4);flex-wrap:wrap;justify-content:center}
.mj-contain .cc-label{font-family:var(--font-meta);font-size:.64rem;letter-spacing:1.5px;
  text-transform:uppercase;color:var(--mj-gray-400)}
.mj-contain a.cc-anchor{font-family:var(--font-display);font-weight:600;font-size:.8rem;text-transform:uppercase;
  color:var(--mj-black);text-decoration:none;padding-bottom:2px;border-bottom:2px solid transparent;transition:.15s}
body.dark-mode .mj-contain a.cc-anchor{color:#f2f2f2}
.mj-contain a.cc-anchor:hover, .mj-contain a.cc-anchor.is-on{border-bottom-color:var(--mj-red);color:var(--mj-red)}
.mj-contain .cc-alt{font-family:var(--font-display);font-weight:600;font-size:.8rem;text-transform:uppercase;
  color:var(--mj-trust);text-decoration:none}
.mj-right{display:flex;align-items:center;justify-content:flex-end;gap:var(--sp-2)}
.mj-icon{background:none;border:none;cursor:pointer;padding:7px;display:flex;color:var(--mj-black);transition:.15s}
body.dark-mode .mj-icon{color:#f2f2f2}
.mj-icon svg{width:20px;height:20px;stroke:currentColor;stroke-width:1.6;fill:none}
.mj-icon:hover{color:var(--mj-red)}
.mj-subscribe{margin-left:var(--sp-3);background:var(--mj-red);color:#fff;text-decoration:none;
  font-family:var(--font-display);font-weight:600;font-size:.78rem;letter-spacing:.4px;text-transform:uppercase;
  padding:9px 17px;border-radius:var(--radius-sm);transition:.15s;white-space:nowrap}
.mj-subscribe:hover{background:var(--mj-red-hover)}
body.dark-mode .mj-subscribe{background:#fff;color:#101012}
body.dark-mode .mj-subscribe:hover{background:var(--mj-red);color:#fff}
.mj-progress{position:absolute;bottom:-1px;left:0;height:3px;width:0;background:var(--mj-red);transition:width .1s linear}
.mj-scrim{position:fixed;inset:0;background:rgba(10,10,10,.5);opacity:0;visibility:hidden;transition:.25s;z-index:12000}
/* ONE OPEN STATE, SPELLED ONE WAY. The mega menu and the search overlay both
   open on `.is-open`; the drawer and its scrim opened on `.open`, and chrome.js
   — written against the majority — set `is-open`. So the drawer's transform
   never fired and the scrim never appeared: markup present, behaviour present,
   and the two agreeing about everything except the name.
   The scrim now follows `body.drawer-open` rather than a class of its own,
   because a backdrop has no state that is not the drawer's state. */
.mj-scrim.is-open, body.drawer-open .mj-scrim{opacity:1;visibility:visible}
.mj-drawer{position:fixed;top:0;left:0;height:100%;width:min(88vw,360px);background:var(--mj-surface);
  transform:translateX(-100%);transition:transform .28s ease;z-index:12001;display:flex;flex-direction:column;
  box-shadow:8px 0 40px rgba(0,0,0,.25)}
body.dark-mode .mj-drawer{background:#101012;color:#fff}
.mj-drawer.is-open{transform:translateX(0)}
/* The page behind a drawer must not scroll under it. */
body.drawer-open{overflow:hidden}

/* --- Site chrome — mega-menu, search, footer states
     (from mashjoy-site-chrome.html) --- */
body.light .mj-logo .logo-light{display:block}
.mj-logo .wordmark{font-family:var(--font-display);font-weight:700;font-size:1.5rem;
  letter-spacing:-.5px;color:var(--mj-black);display:none}
body.dark-mode .mj-logo .wordmark{color:#fff}
.mj-logo .wordmark b{color:var(--mj-red);font-weight:700}
.mj-nav{display:flex;justify-content:center}
/* THE BAR'S OWN LIST, AND ONLY THAT ONE (fixed 2026-09-16).
   These were written unscoped — `.mj-nav ul` and `.mj-nav li` — so the header
   bar's 76px height and its row layout reached every list INSIDE the mega menu
   too. Same specificity as `.mm-col ul` and later in the file, so the bar won:
   a column of five links was given a 76px box, its items painted straight
   through the column below, and it has been shipping that way on every page
   with a mega menu. It only became obvious when the deals band was removed and
   the columns were the only thing left to look at.
   `position:static` stays deliberate on the top-level item: it is what makes the
   panel position against the nav instead of against the item it opens from. */
.mj-nav>ul{display:flex;gap:var(--sp-6);list-style:none;margin:0;padding:0;height:76px}
.mj-nav>ul>li{display:flex;align-items:center;position:static}
.mj-nav>ul>li>a{font-family:var(--font-display);font-weight:600;font-size:.82rem;
  letter-spacing:.4px;text-transform:uppercase;color:var(--mj-black);text-decoration:none;
  display:flex;align-items:center;gap:6px;height:100%;border-bottom:2px solid transparent;transition:.15s}
body.dark-mode .mj-nav>ul>li>a{color:#f2f2f2}
.mj-nav>ul>li>a .chev{width:12px;height:12px;stroke:currentColor;stroke-width:2.5;fill:none;opacity:.55;transition:transform .2s}
.mj-nav>ul>li:hover>a{color:var(--mj-red);border-bottom-color:var(--mj-red)}
.mj-nav>ul>li:hover>a .chev{transform:rotate(180deg)}
.mj-nav>ul>li:hover .mm{opacity:1;visibility:visible;transform:translateY(0)}
body.nav-contain .mj-nav{display:none}
body.nav-contain .mj-contain{display:flex}
/* ── THE READING COMPANION · observations 11.3 (and the bar's own bug) ────
   MEASURED FIRST. The bar rendered its FULL SECTION HEADINGS as nav items —
   32 to 52 characters each — so four anchors sat on FOUR ROWS and the bar grew
   to 151px inside a 73px sticky header. It overflowed the header it lives in,
   over the article title, on every contained page. "Compact, not space-hungry"
   was not a preference; the thing was broken.

   THE FIX IS NOT SHORTER LABELS. Cutting those headings to fit produced "Two
   companies, two" and "What else the company" — an ellipsis promising a longer
   label that exists nowhere. A heading earns its length in the body.

   So the bar shows WHERE YOU ARE, in full, and keeps the list behind a
   disclosure — which is what 11.3 asked for in the first place: "You are
   reading …", horizontal, compact, collapsible, "a helpful reading companion,
   not an intrusive toolbar". One row, always. Native <details>, so it works
   before the script loads and with it switched off. */
/* THE BAR IS TWO GROUPS, NOT SIX ITEMS.
   Lucky asked for "the best arrangement or alignment of all the elements … so
   that there is balance". Left: where you are, which is prose and should take
   whatever room is going. Right: the two controls, which are fixed-width and
   must not be squeezed by a long heading. `1fr` on the label group is what lets
   the heading grow instead of truncating at a fixed 32 characters. */
/* NO `display` HERE — THAT WAS MINE, AND IT BROKE EVERY OTHER SURFACE.
   The base rule is `.mj-contain{display:none}` and `body.nav-contain` turns it
   on, because the containment bar belongs to CONTAINED pages only. Rebuilding
   this as the reading companion I wrote `display:flex` unconditionally, so the
   bar became a live grid item on the homepage and every archive — taking the
   third of `.mj-bar`'s three columns and pushing search, the theme toggle and
   Subscribe onto an implicit SECOND ROW, on top of the hero.

   That is exactly what Lucky screenshotted. The layout properties stay; the
   display decision goes back to the state rules that own it. */
.mj-contain{ flex-wrap:nowrap; min-width:0; width:100%;
  align-items:center; gap:var(--sp-4); justify-content:flex-start }

/* THE CONTAIN BAR WAS THE THIRD CHROME ELEMENT PUSHING EVERY ARTICLE SIDEWAYS
   (2026-08-27). `flex-wrap:nowrap` with three fixed-width children — the label,
   the section dropdown and the deals panel — totals ~418px, and with
   `overflow-x:visible` the surplus grew the document instead of scrolling. On a
   phone the honest behaviour for a strip of chips is to scroll like a tab bar,
   and the eyebrow label is the one child that carries no information a reader
   needs, so it goes rather than eating the room. */
@media(max-width:760px){
  .mj-contain{ overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch;
    overscroll-behavior-x:contain; scrollbar-width:none; gap:var(--sp-3) }
  .mj-contain::-webkit-scrollbar{ display:none }
  .mj-contain .cc-label{ display:none }
}

/* THE CHIP STRIP WAS 40px WIDE ON A PHONE (2026-08-28). Making it scroll stopped
   the sideways page scroll, and I called that fixed. It was not: `.mj-bar` is a
   three-column grid, and at 375px the logo takes 187px and the icon cluster 76,
   so the middle column — the reader's map of the article, and the "Prices &
   deals" panel — got 40px. A scroll container narrower than one chip is not a
   scroll container. Measured on the built page: `.cc-offers` started at x=362
   and ran to 500, entirely off-screen, and its children overlapped the icons.

   On a phone the strip needs its own row, so it gets one. Scoped to
   `body.nav-contain` because only contained pages have a strip to place, and
   `--mj-header-top` is moved WITH it — that variable is what every anchor jump
   and the sticky rail measure against, and this article's whole "All sections"
   menu is anchor jumps. It is set through `:root:has()` because
   `scroll-padding-top` lives on <html>, an ancestor of the body that carries
   the state class. */
@media(max-width:560px){
  :root:has(body.nav-contain){ --mj-header-top:85px }   /* measured 84px + its 1px rule */
  body.nav-contain .mj-bar{ height:auto; grid-template-columns:1fr auto;
    row-gap:0; column-gap:var(--sp-3); padding-top:4px; padding-bottom:4px }
  body.nav-contain .mj-left{ grid-column:1; grid-row:1 }
  body.nav-contain .mj-right{ grid-column:2; grid-row:1 }
  body.nav-contain .mj-contain{ grid-column:1 / -1; grid-row:2;
    width:auto; padding-bottom:4px }
}
.mj-contain .cc-label{ flex:0 0 auto }
.mj-contain .cc-now{ flex:1 1 auto; font-family:var(--font-display); font-weight:700;
  font-size:.82rem; color:var(--mj-black);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  min-width:0; max-width:none; margin-right:auto }
body.dark-mode .mj-contain .cc-now{ color:#f2f2f2 }

.cc-sections{ position:relative; flex:0 0 auto }
.cc-sections>.cc-more{ list-style:none; cursor:pointer;
  font-family:var(--font-display); font-weight:600; font-size:.74rem;
  letter-spacing:.6px; text-transform:uppercase; color:var(--mj-text-muted);
  white-space:nowrap; padding:5px 9px; border:1px solid var(--mj-rule);
  border-radius:var(--radius-sm) }
.cc-sections>.cc-more::-webkit-details-marker{ display:none }
.cc-sections>.cc-more::after{ content:" \25be" }
.cc-sections[open]>.cc-more::after{ content:" \25b4" }
.cc-sections>.cc-more:hover{ color:var(--mj-red); border-color:var(--mj-red) }
.cc-sections .cc-list{ position:absolute; top:calc(100% + 8px); left:0; z-index:70;
  display:grid; gap:2px; min-width:min(320px,80vw); padding:var(--sp-3);
  background:var(--mj-surface); border:1px solid var(--mj-rule);
  border-radius:var(--radius-sm); box-shadow:0 10px 30px rgba(0,0,0,.16) }
body.dark-mode .cc-sections .cc-list{ background:#17171a; border-color:#2a2a2d }
/* THE SECTION PANEL OPENED OFF-SCREEN ON A PHONE (2026-08-27). It is anchored
   `left:0` to a strip item that sits near the right edge, so a 320px panel
   extended past the viewport and grew the document — the contain bar became the
   third chrome element to push every article sideways. Anchor it to the right
   below the tablet breakpoint and cap it to the viewport. */
@media(max-width:760px){
  .cc-sections .cc-list{ left:auto; right:0;
    min-width:min(280px, calc(100vw - 2 * var(--sp-4))); max-width:calc(100vw - 2 * var(--sp-4)) }
}
/* inside the panel the headings get their full length back — there is room, and
   a list of sections is the one place the whole heading is the useful thing */
.cc-sections .cc-list a.cc-anchor{ white-space:normal; text-transform:none;
  font-size:.82rem; font-weight:600; padding:6px 8px; border:0; line-height:1.35 }
.cc-sections .cc-list a.cc-anchor:hover{ background:var(--mj-surface-2); color:var(--mj-red) }
body.dark-mode .cc-sections .cc-list a.cc-anchor:hover{ background:#22222a }

@media(max-width:900px){
  .mj-contain .cc-now{ max-width:18ch }
  .mj-contain .cc-alt{ display:none }
}

/* ── WHAT THESE COST · observation 11.2, and my answer on the mega-menu ──
   Lucky asked for the reading header to become "a purchase decision support
   tool" — product menus with price, recommendation, discount and a buy button.

   I pushed back on a PERMANENT commercial bar following the reader through the
   article, and I owe the reason rather than the refusal. It is his own rule,
   from 2.1.1: "we are not always trying to just sell, but to sell in an
   interactive way that doesn't necessarily bother those who are mainly trying to
   learn from what they are reading first." A sell surface pinned above every
   paragraph is the one thing that sentence rules out.

   AND THE LIBRARY ALREADY AGREED WITH HIM, NOT WITH THE MEGA-MENU. `.cc-offers`
   has been in the chrome prototype all along as a SINGLE closed item — "Current
   Offers ▾" — beside the section nav. So this is not a compromise between two
   views; it is 11.2's content delivered through the mechanism both the owner and
   the library already specified: closed by default, one click, full detail.

   Every field 11.2 asks for is here — name, price, what we recommend it for, the
   offer, and the row itself is the buy link. THE OFFER LINE RENDERS ONLY FROM A
   VERIFIED OFFER, so today it is absent everywhere, and the panel is a price
   list until the ledger says otherwise. */
.cc-offers{ position:relative; flex:0 0 auto }
.cc-offers>.cc-buy{ list-style:none; cursor:pointer;
  font-family:var(--font-display); font-weight:600; font-size:.78rem;
  letter-spacing:.6px; text-transform:uppercase; color:#fff;
  background:var(--mj-growth); padding:6px 12px; border-radius:var(--radius-sm);
  white-space:nowrap }
.cc-offers>.cc-buy::-webkit-details-marker{ display:none }
.cc-offers>.cc-buy::after{ content:" \25be" }
.cc-offers[open]>.cc-buy::after{ content:" \25b4" }
.cc-offers>.cc-buy:hover{ background:var(--mj-ink-900) }
.cc-buylist{ position:absolute; top:calc(100% + 8px); right:0; z-index:70;
  display:grid; gap:5px; width:min(460px,92vw); padding:var(--sp-4);
  background:var(--mj-surface); border:1px solid var(--mj-rule);
  border-radius:var(--radius-sm); box-shadow:0 14px 38px rgba(0,0,0,.20) }
body.dark-mode .cc-buylist{ background:#17171a; border-color:#2a2a2d }
.cc-buylist__k{ margin:0 0 2px; font-family:var(--font-meta); font-size:.58rem;
  letter-spacing:1.3px; text-transform:uppercase; font-weight:700;
  color:var(--mj-text-muted) }

/* ── THE DEAL ROW · a product box compressed to a menu ────────────────────
       "a mini horizontal product box element where people can clearly see their
        current price and the extra deal that they get … and then also the price
        that they will end up paying if they took the offer and then with also a
        CTA button to buy … designed in a small way that will end up fitting
        properly as a dropdown element."                              — Lucky

   Four columns of meaning in one 52px-tall row: WHAT IT IS (shot), WHAT IT IS
   FOR (name + fit line), WHAT OUR LINK IS WORTH (the offer), and WHAT YOU PAY
   (struck price, real price, action). The whole row is the tracked link, so the
   button is a target and not a second decision.

   THE STRUCK PRICE AND THE OFFER LINE RENDER ONLY FROM A VERIFIED OFFER. With
   none in the ledger the row collapses to shot / name / fit / price / CTA and
   stays honest — the panel is a price list until the desk has a discount to
   stand behind. */
.cc-deal{ display:grid; grid-template-columns:52px minmax(0,1fr) auto;
  align-items:center; gap:var(--sp-3); padding:9px 10px; text-decoration:none;
  border:1px solid var(--mj-rule); border-radius:var(--radius-sm);
  background:var(--mj-surface); transition:border-color .14s ease, background .14s ease }
body.dark-mode .cc-deal{ border-color:#2a2a2d; background:#1c1c20 }
.cc-deal:hover{ border-color:var(--mj-red); background:var(--mj-surface-2) }
body.dark-mode .cc-deal:hover{ background:#22222a }
/* A DEAL ROW WITHOUT A PRODUCT SHOT MUST NOT KEEP THE SHOT'S COLUMN.
   Third time this exact shape has bitten: a grid declares a fixed first track
   for an image, the template makes that image CONDITIONAL, and every row that
   lacks one shifts its children one column left — the body lands in the 52px
   thumbnail track and wraps itself into a two-word ribbon while the price and
   button land where the body belonged. It happened to `.mj-pb--slim` in the
   reveal panel, and it is happening here to every product in the header's
   price panel that has no shot yet: WAPPoint's four machines and the Counter
   bundle, which is most of the list.

   The structure IS the fact, so the stylesheet reads the structure rather than
   waiting for a renderer to label it. One rule per element, and every row is
   fixed at once — including the ones nobody has looked at.

   THE GENERAL RULE, now that it has recurred: any grid whose first track is
   sized for an OPTIONAL child needs a `:has()` companion. See trap 30. */
.cc-deal:not(:has(> .cc-deal__shot)){ grid-template-columns:minmax(0,1fr) auto }
.cc-deal__shot{ display:block; width:52px; height:52px; background:#fff;
  border-radius:var(--radius-sm); overflow:hidden }
.cc-deal__shot img{ width:100%; height:100%; object-fit:contain; padding:4px;
  box-sizing:border-box; display:block }
.cc-deal__body{ min-width:0 }
.cc-deal__name{ display:block; font-family:var(--font-display); font-weight:700;
  font-size:.86rem; color:var(--mj-black); line-height:1.25 }
body.dark-mode .cc-deal__name{ color:#fff }
.cc-deal__for{ display:block; margin-top:1px; font-size:.72rem; line-height:1.4;
  color:var(--mj-text-muted); overflow:hidden; text-overflow:ellipsis;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical }
/* what our link is worth — the only reason a reader should click ours */
.cc-deal__offer{ display:inline-block; margin-top:4px; padding:1px 6px;
  background:var(--mj-growth-subtle); color:var(--mj-growth-text);
  border-radius:var(--radius-sm); font-family:var(--font-meta); font-size:.56rem;
  letter-spacing:.9px; text-transform:uppercase; font-weight:700 }
body.dark-mode .cc-deal__offer{ background:#12261f; color:#4EC98A }
.cc-deal__money{ display:grid; justify-items:end; gap:1px; text-align:right }
.cc-deal__was{ font-family:var(--font-meta); font-size:.62rem;
  color:var(--mj-gray-400) }
.cc-deal__now{ font-family:var(--font-display); font-weight:800; font-size:1rem;
  color:var(--mj-black); line-height:1.1 }
body.dark-mode .cc-deal__now{ color:#fff }
.cc-deal__cta{ margin-top:3px; padding:4px 10px; background:var(--mj-red);
  color:#fff; border-radius:var(--radius-sm); font-family:var(--font-display);
  font-weight:700; font-size:.62rem; letter-spacing:.8px; text-transform:uppercase;
  white-space:nowrap }
.cc-deal:hover .cc-deal__cta{ background:var(--mj-ink-900) }

/* coupon codes, when a programme actually publishes one */
.cc-coupons{ grid-column:1 / -1; margin:4px 0 0; padding:8px 10px;
  background:var(--mj-surface-2); border-left:3px solid var(--mj-growth);
  border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  font-size:.76rem; line-height:1.5; color:var(--mj-ink) }
body.dark-mode .cc-coupons{ background:#22222a; color:#e8e8ea }
.cc-coupons__k{ display:block; font-family:var(--font-meta); font-size:.56rem;
  letter-spacing:1.2px; text-transform:uppercase; color:var(--mj-growth-text) }
.cc-coupons__body{ font-variant-numeric:tabular-nums }

.cc-deals{ grid-column:1 / -1; margin:6px 0 0; padding:9px 10px;
  background:var(--mj-surface-2); border-radius:var(--radius-sm);
  font-size:.76rem; line-height:1.5; color:var(--mj-text-muted) }
body.dark-mode .cc-deals{ background:#22222a }
.cc-deals__k{ display:block; font-family:var(--font-meta); font-size:.58rem;
  letter-spacing:1.2px; text-transform:uppercase; color:var(--mj-growth-text);
  margin-bottom:2px }

.cc-buy__offer{ grid-column:1 / -1; margin-top:3px; font-family:var(--font-meta);
  font-size:.6rem; letter-spacing:1px; text-transform:uppercase; font-weight:700;
  color:var(--mj-growth-text) }

.mj-contain .cc-offers{position:relative}
.mj-contain .cc-offers>a{font-family:var(--font-display);font-weight:600;font-size:.82rem;text-transform:uppercase;
  color:#fff;background:var(--mj-growth);padding:7px 14px;border-radius:var(--radius-sm);text-decoration:none;display:inline-flex;gap:6px;align-items:center}
.mj-footer{background:var(--mj-ink-900);color:#e8e8ea;border-top:4px solid var(--mj-red)}

/* --- Product box — 4 layouts (prototype vocabulary, drives Elementor)
     (from mashjoy-product-box.html) --- */
.mj-pb{font-family: var(--font-body); color: var(--mj-ink);}
.mj-pb__eyebrow{font-family: var(--font-meta); font-size: .7rem; letter-spacing: 1.5px;
    text-transform: uppercase; color: var(--mj-gray-400); margin: 0 0 6px;}
.mj-pb__name{font-family: var(--font-display); font-weight: 800; letter-spacing: -.01em; color: var(--mj-black); line-height: 1.12; margin: 0;}
/* A PRICE CLUSTER IS A ROW OF UNLIKE THINGS — a big number, a struck number, a
   unit, sometimes a chip — and inline flow aligns them by their letters rather
   than by themselves. Same fix as `.mj-offer`, same reason: the struck price
   sat 3.3px below the price it was struck against. Centred, so everything in
   the cluster belongs to one line. */
.mj-pb__price{ display:inline-flex; align-items:center; flex-wrap:wrap; gap:8px;
  font-family: var(--font-display); font-weight: 800; color: var(--mj-black); line-height: 1;}
.mj-pb__price .was{font-size: .55em; color: var(--mj-gray-400); text-decoration: line-through; font-weight: 500; margin-left: 8px; letter-spacing: 0;}
.mj-pb__price .unit{font-size: .42em; color: var(--mj-gray-400); font-weight: 500; letter-spacing: 0;}
.mj-pb__specs{list-style: none; margin: 0; padding: 0;}
.mj-pb__specs li{display: flex; justify-content: space-between; gap: 16px; padding: 9px 0; border-bottom: 1px solid var(--mj-rule); font-size: .95rem;}
.mj-pb__specs li:last-child{border-bottom: none;}
.mj-pb__specs .k{color: var(--mj-text-muted);}
/* THE VALUE IS THE ANSWER, NOT A SHOUT. Lucky: "the text that appears in bold to
   the right of elements ... feels like it's too much on the face."
   He is describing what bold does to a SENTENCE. A spec value is sometimes a
   number worth scanning ("R699", "R0") and sometimes a clause ("2.30% excl. VAT
   local cards; 1.35% on debit above R50k/month") — and 700-weight pure black
   suited the first and buried the reader in the second. Dropped to 600 on
   charcoal, capped to a comfortable measure so a long value wraps instead of
   stretching the row, and given tabular figures so the numbers line up down the
   column. Still clearly the answer; no longer shouting it. */
/* THE NUMBERS, ONE CLICK AWAY — Lucky, 2026-08-23.
   "Technical specs and other things can still be there but can be toggled on if
   somebody really wants to see them, because they are sort of a repeat from
   what they will have already read from the vendor."
   A spec table is the least persuasive thing in a product box and the tallest.
   Closed by default the box becomes what it is for: the name, the reason, the
   price, and what our link adds. Nothing is removed — a reader comparing two
   machines opens both. */
.mj-pb__specwrap{ margin:10px 0 0 }
/* THE SPEC TOGGLE IS A CONTROL, AND IT LOOKS LIKE THE OTHER ONE.
   Lucky, 2026-08-26: give it "the same formatting and the icon as: And
   everything Yoco gives everyone". Two disclosures in one box that look like
   different species is two designers. Same face, same marker, same size — the
   only difference is colour, because this one reveals FACTS about the product
   rather than more of what our route is worth, and grey says so. */
.mj-pb__specsum{ cursor:pointer; font-family:var(--font-display); font-weight:700;
  font-size:.82rem; letter-spacing:.01em; text-transform:none;
  color:var(--mj-charcoal); list-style:none;
  display:flex; align-items:center; gap:8px; padding:4px 0 }
.mj-pb__specsum::-webkit-details-marker{ display:none }
.mj-pb__specsum::before{ content:"+"; display:inline-flex; align-items:center;
  justify-content:center; width:18px; height:18px; flex:0 0 18px; border-radius:5px;
  background:var(--mj-charcoal); color:#fff; font-family:var(--font-display);
  font-weight:800; font-size:.8rem; line-height:1 }
.mj-pb__specwrap[open] .mj-pb__specsum::before{ content:"\2212" }
.mj-pb__specsum:hover{ filter:brightness(1.35) }
.mj-pb__specsum:focus-visible{ outline:2px solid var(--mj-charcoal); outline-offset:3px;
  border-radius:5px }
body.dark-mode .mj-pb__specsum{ color:#e9e9ec }
body.dark-mode .mj-pb__specsum::before{ background:#e9e9ec; color:#141416 }
.mj-pb__specwrap .mj-pb__specs{ margin-top:8px }
.mj-pb__specs .v{ font-weight:600; color:var(--mj-charcoal); text-align:right;
  max-width:60%; font-variant-numeric:tabular-nums }
.mj-pb__verified{font-family: var(--font-meta); font-size: .72rem; color: var(--mj-gray-400); margin: 12px 0 0;}
.mj-pb__badge{display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-display); font-weight: 800; font-size: .68rem; letter-spacing: .5px; text-transform: uppercase; padding: 5px 10px; border-radius: var(--radius-sm);}
.mj-pb__badge--pick{background: var(--mj-growth); color: #fff;}
.mj-pb__badge--verified{background: var(--mj-growth-subtle); color: #08694c;}
/* WAS THE ALERT COLOUR. A saving is not an alert, and rendering it red here
   while the same saving was green two elements up gave one fact two meanings.
   Appearance now comes from the shared deal-tag rule. */
body.dark-mode .mj-pb__badge--verified{background: rgba(16,185,129,.16); color: #34d8a6;}
body.dark-mode .mj-pb__badge--deal{background: rgba(193,18,31,.18); color: #ff8b93;}
.mj-pb__cta{display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
    font-size: .85rem; letter-spacing: .3px; padding: 13px 22px;
    background: var(--link-action); color: #fff; border: none; border-radius: var(--radius-sm);
    cursor: pointer; transition: background .18s ease;}
.mj-pb__cta:hover{background: var(--mj-red-hover);}
.mj-pb__cta--ghost{background: transparent; color: var(--mj-black); border: 1px solid var(--mj-gray-300);}
.mj-pb__cta--ghost:hover{background: transparent; border-color: var(--link-action); color: var(--link-action);}
body.dark-mode .mj-pb__cta--ghost{color: #fff; border-color: #444;}
.mj-pb__disclosure{font-family: var(--font-meta); font-size: .68rem; color: var(--mj-gray-400); margin: 8px 0 0;}
.mj-pb__img{background: var(--mj-white); border: 1px solid var(--mj-rule); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; overflow: hidden; position: relative;}
.mj-pb__img::after{content: attr(data-label); position: absolute; bottom: 8px; left: 8px; right: 8px; font-family: var(--font-meta); font-size: .62rem; letter-spacing: .5px; color: var(--mj-gray-400); text-transform: uppercase; text-align: center;}
.mj-pb__img svg{width: 46%; height: 46%; opacity: .5;}
.mj-pb__img img{width: 100%; height: 100%; object-fit: contain; padding: 12px; display: block;}
.mj-pb.mj-pb--slim .mj-pb__img img, .mj-pb--compare .ch-img img{padding: 6px;}
body.dark-mode .mj-pb__img{background: #141414; border-color: #2a2a2a;}
.mj-pb--card{background: var(--mj-surface); border: 1px solid var(--mj-rule); border-radius: var(--radius-lg); box-shadow: 0 2px 10px rgba(0,0,0,.03);}
body.dark-mode .mj-pb--card{background: #131313; border-color: #2a2a2a; box-shadow: none;}
/* THE SLIM ROW IS BRACKETED THE SAME WAY (Lucky, 2026-09-16).
   "Also improve these slim elements so that their borders adopt the product
   vendor colour … and since we improved the hero to have the border at the top
   and the bottom, make the slim versions the same. Just for consistency."
   He is right, and the reason is the one above: a left rule already means the
   short answer. One bracket, one meaning, whatever the size of the box.
   `--link-action` was the old rule's colour — a generic blue that named no
   vendor at all. Falls back to ink where no vendor is given. */
.mj-pb.mj-pb--slim{display: grid; grid-template-columns: 64px 1fr auto; gap: 18px;
  align-items: center; padding: 16px 20px; margin: 24px 0;
  --brand-accent: var(--mj-ink-900);
  background: var(--mj-surface);
  border-left: 1px solid var(--mj-rule); border-right: 1px solid var(--mj-rule);
  border-top: 3px solid var(--brand-accent);
  border-bottom: 3px solid var(--brand-accent);
  border-radius: var(--radius-md);}
body.dark-mode .mj-pb--slim{background: #131313; border-color: #2a2a2a;}
.mj-pb.mj-pb--slim .mj-pb__img{width: 64px; height: 64px; aspect-ratio: 1/1;}
.mj-pb.mj-pb--slim .mj-pb__img::after{display: none;}
.mj-pb.mj-pb--slim .mj-pb__name{font-size: 1.05rem; margin: 0;}
.mj-pb.mj-pb--slim .mj-pb__hook{font-size: .88rem; color: var(--mj-text-muted); margin: 3px 0 0;}
.mj-pb.mj-pb--slim .mj-pb__right{display: flex; align-items: center; gap: 18px;}
/* the price and its struck original, addressable as two facts */
/* the slim row's middle column — badge, name, hook. It carried no rule at all,
   because the row had never rendered on the site: the L0 gate caught it the
   moment it did. */
/* ── ONE MARKER PER SURFACE · owner note, 2026-08-21 ─────────────────────
       "there is a black border to the left and then the two elements also have
        red borders in them … I just don't know which is good practice — should
        we have the borders only in the inset elements, or also in the main
        background element?"                                          — Lucky

   Three nested boxes were each claiming to be the container: the revealed panel
   with a 3px ink bar, and two product rows with 3px red bars, all within about
   twenty pixels of each other and all on the same white ground. Each border is
   defensible on its own, which is exactly how this happens.

   THE ANSWER TO HIS QUESTION: the marker belongs to the thing that APPEARED.
   A left accent bar means "this block has a voice" — a note, a caveat, a tray.
   The panel is the voice here: it is what the reader summoned and what ties back
   to the sentence they pressed. Its contents are objects INSIDE it, and an
   object inside a marked container does not need to announce itself again.

   So a slim row keeps its red bar when it stands alone in an article — where it
   is the block with the voice — and drops it inside any container that already
   carries one. It keeps its hairline border, so it is still a discrete card. */
.mj-ireveal__p .mj-pb,
.mj-note__tray .mj-pb,
.mj-pair__cards .mj-pb{ border-left-width:1px; border-left-color:var(--mj-rule) }
body.dark-mode .mj-ireveal__p .mj-pb,
body.dark-mode .mj-note__tray .mj-pb,
body.dark-mode .mj-pair__cards .mj-pb{ border-left-color:#2a2a2d }

/* ── A ROW THAT REFUSES TO BE CRUSHED ────────────────────────────────────
   The pair grid put this five-part horizontal row into a 290px column at every
   desktop width, and NOTHING CAUGHT IT. All seventeen gates passed: the markup
   was valid, the slots were filled, the offer was verified, the payload
   reconciled. The row simply wrapped its hook one word per line and slid the
   price under the product shot. A build gate reads structure; only a layout
   knows it has run out of room.

   So the row now carries the knowledge itself. Any container that opts in below
   becomes a query container, and a row inside one narrower than 420px folds to
   two lines — image and words on the first, price and action on the second —
   instead of mangling five columns into three inches. This is the first
   container query in the library; it is here because the alternative is to
   remember, every time someone nests this row somewhere new, that it needs
   about 420px to be itself.

   Browsers without @container simply keep today's behaviour, and today's two
   containers are both wide enough. */
.mj-ireveal__p,
.mj-note__tray,
.mj-pair__cards{ container-type:inline-size; container-name:mj-slot }

@container mj-slot (max-width:420px){
  .mj-pb--slim,
  .mj-pb.mj-pb--slim{ grid-template-columns:58px minmax(0,1fr); row-gap:var(--sp-3) }
  .mj-pb--slim .mj-pb__right{ grid-column:1 / -1; display:flex; align-items:center;
    justify-content:space-between; gap:var(--sp-4) }
  .mj-pb--slim .mj-pb__cta{ flex:0 0 auto }
}

.mj-pb__mid{ min-width:0 }
.mj-pb__mid .mj-pb__name{ margin:0 }

.mj-pb--slim .mj-pb__now{ font-family:var(--font-display); font-weight:800 }
.mj-pb--slim .was{ margin-left:6px; font-family:var(--font-meta); font-size:.68rem;
  font-weight:400; color:var(--mj-gray-400); text-decoration:line-through }

.mj-pb.mj-pb--slim .mj-pb__price{font-size: 1.35rem; white-space: nowrap;}
.mj-pb.mj-pb--slim .mj-pb__cta{padding: 10px 18px; font-size: .78rem;}
.mj-pb-grid{display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin: 28px 0;}
.mj-pb.mj-pb--stacked{display: flex; flex-direction: column; padding: 20px;}
.mj-pb.mj-pb--stacked .mj-pb__img{aspect-ratio: 4/3; margin-bottom: 16px;}
.mj-pb.mj-pb--stacked .mj-pb__badges{display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; min-height: 24px;}
.mj-pb.mj-pb--stacked .mj-pb__name{font-size: 1.25rem; margin-bottom: 6px;}
.mj-pb.mj-pb--stacked .mj-pb__hook{font-size: .9rem; color: var(--mj-text-muted); line-height: 1.5; margin: 0 0 14px;}
.mj-pb.mj-pb--stacked .mj-pb__price{font-size: 1.7rem; margin-bottom: 14px;}
.mj-pb.mj-pb--stacked .mj-pb__specs{margin-bottom: 16px;}
.mj-pb.mj-pb--stacked .mj-pb__foot{margin-top: auto;}
.mj-pb.mj-pb--stacked .mj-pb__cta{width: 100%;}
.mj-pb-compare-scroll{overflow-x: auto; margin: 28px 0; -webkit-overflow-scrolling: touch; border-radius: var(--radius-lg);}
.mj-pb--compare{min-width: 620px; width: 100%; border-collapse: separate; border-spacing: 0; background: var(--mj-surface); border: 1px solid var(--mj-rule); border-radius: var(--radius-lg); overflow: hidden;}
body.dark-mode .mj-pb--compare{background: #131313; border-color: #2a2a2a;}
.mj-pb--compare th, .mj-pb--compare td{padding: 16px 18px; text-align: left; border-bottom: 1px solid var(--mj-rule); vertical-align: top;}
.mj-pb--compare thead th{background: var(--mj-surface-2);}
body.dark-mode .mj-pb--compare thead th{background: #191919;}
.mj-pb--compare .row-label{font-family: var(--font-meta); font-size: .72rem; letter-spacing: .5px; text-transform: uppercase; color: var(--mj-text-muted); width: 150px; white-space: nowrap;}
.mj-pb--compare .col-head{min-width: 210px;}
.mj-pb--compare .col-head.is-winner{background: var(--mj-growth-subtle);}
body.dark-mode .mj-pb--compare .col-head.is-winner{background: rgba(16,185,129,.10);}
.mj-pb--compare .ch-img{width: 56px; height: 56px; aspect-ratio: 1/1; margin-bottom: 10px;}
.mj-pb--compare .ch-img::after{display: none;}
.mj-pb--compare .ch-name{font-family: var(--font-display); font-weight: 800; color: var(--mj-black); font-size: 1.1rem; line-height: 1.15;}
.mj-pb--compare .ch-hook{font-size: .82rem; color: var(--mj-text-muted); margin-top: 4px;}
.mj-pb--compare td .v{font-weight: 700; color: var(--mj-black);}
.mj-pb--compare td .sub{display: block; font-size: .8rem; color: var(--mj-gray-400); font-weight: 400; margin-top: 2px;}
.mj-pb--compare tr:last-child td{border-bottom: none;}
.mj-pb--compare .cell-win{color: #08694c; font-weight: 800;}
body.dark-mode .mj-pb--compare .cell-win{color: #34d8a6;}
.mj-pb--compare .mj-pb__cta{width: 100%;}
.mj-pb-compare-hint{font-family: var(--font-meta); font-size: .7rem; color: var(--mj-gray-400); text-align: center; margin: 6px 0 0;}
@media (max-width: 720px){
  .mj-pb.mj-pb--slim{grid-template-columns: 48px 1fr;}
  .mj-pb.mj-pb--slim .mj-pb__img{width: 48px; height: 48px;}
  .mj-pb.mj-pb--slim .mj-pb__right{grid-column: 1 / -1; justify-content: space-between; margin-top: 4px;}
  .mj-pb-grid{grid-template-columns: 1fr;}
}

/* --- Review block — compact / sidebar / full (prototype vocabulary)
     (from mashjoy-review-block.html) --- */
.scms-rb{font-family:var(--font-body);color:var(--mj-ink);
  --rb-accent:var(--mj-red);}
.scms-rb *{box-sizing:border-box}
.scms-rb-score{display:inline-flex;align-items:baseline;gap:.25rem;
  font-family:var(--font-display);font-weight:800;line-height:1}
.scms-rb-score b{font-size:1.5rem;color:var(--mj-ink)}
.scms-rb-score span{font-size:.85rem;color:var(--mj-gray-400);font-weight:700}
body.dark-mode .scms-rb-score b{color:#f4f4f5}
.scms-rb-badge{display:inline-flex;align-items:center;gap:4px;
  font-family:var(--font-meta);font-size:.66rem;letter-spacing:1px;text-transform:uppercase;
  font-weight:700;padding:3px 8px;border-radius:2px;
  background:var(--mj-growth-subtle);color:#0b7a5b}
.scms-rb-badge.is-alt{background:var(--mj-trust-subtle);color:var(--mj-trust)}
body.dark-mode .scms-rb-badge{background:rgba(16,185,129,.16);color:#4ade9f}
body.dark-mode .scms-rb-badge.is-alt{background:rgba(37,99,235,.18);color:#7fa8ff}
.scms-rb-cta{display:inline-flex;align-items:center;justify-content:center;gap:.4rem;
  font-family:var(--font-display);font-weight:800;text-transform:uppercase;font-size:.8rem;
  letter-spacing:.02em;padding:11px 20px;border-radius:var(--radius-sm);
  background:var(--link-action);color:#fff;border:none;cursor:pointer;transition:.15s}
.scms-rb-cta:hover{background:var(--mj-red-hover);color:#fff}
.scms-rb-cta.is-ghost{background:transparent;color:var(--mj-ink);border:1px solid var(--mj-gray-300)}
.scms-rb-cta.is-ghost:hover{border-color:var(--mj-red);color:var(--mj-red)}
body.dark-mode .scms-rb-cta.is-ghost{color:#e9e9ea;border-color:#3a3a3d}
.scms-rb-src{color:var(--mj-trust);font-family:var(--font-meta);font-size:.72rem;
  letter-spacing:.5px;border-bottom:1px solid transparent}
.scms-rb-src:hover{border-bottom-color:var(--mj-trust)}
.scms-rb-checked{color:var(--mj-gray-400);font-family:var(--font-meta);font-size:.7rem;letter-spacing:.5px}
.scms-rb-heading{font-family:var(--font-display);font-weight:800;font-size:1.02rem;
  letter-spacing:-.01em;margin:0;color:var(--mj-ink)}
body.dark-mode .scms-rb-heading{color:#f4f4f5}
.scms-rb-eyebrow{font-family:var(--font-meta);font-size:.68rem;letter-spacing:1.5px;
  text-transform:uppercase;color:var(--mj-gray-400);margin:0 0 2px}
.scms-rb.scms-rb--compact{max-width:var(--article-width);margin:var(--sp-6) auto;
  border:1px solid var(--mj-gray-200);border-left:3px solid var(--rb-accent);
  border-radius:var(--radius-md);background:var(--mj-surface);
  padding:var(--sp-5);display:grid;grid-template-columns:auto 1fr auto;
  gap:var(--sp-5);align-items:center}
body.dark-mode .scms-rb--compact{background:#161618;border-color:#2a2a2d}
.scms-rb.scms-rb--compact .rb-c-score{text-align:center;padding-right:var(--sp-5);
  border-right:1px solid var(--mj-gray-200)}
body.dark-mode .scms-rb--compact .rb-c-score{border-color:#2a2a2d}
.scms-rb.scms-rb--compact .rb-c-score .scms-rb-score{flex-direction:column;align-items:center;gap:0}
.scms-rb.scms-rb--compact .rb-c-score .scms-rb-score b{font-size:2rem}
.scms-rb.scms-rb--compact .rb-c-body .scms-rb-heading{font-size:1.15rem;margin-bottom:2px}
.scms-rb.scms-rb--compact .rb-c-verdict{font-size:.98rem;line-height:1.5;margin:.35rem 0 .5rem;
  color:var(--mj-charcoal)}
body.dark-mode .scms-rb--compact .rb-c-verdict{color:#c9c9cc}
.scms-rb.scms-rb--compact .rb-c-fit{display:flex;flex-wrap:wrap;gap:6px 14px;
  font-size:.82rem;line-height:1.4}
.scms-rb.scms-rb--compact .rb-c-fit b{font-family:var(--font-meta);text-transform:uppercase;
  letter-spacing:.5px;font-size:.66rem}
.scms-rb.scms-rb--compact .rb-c-fit .is-for b{color:#0b7a5b}
.scms-rb.scms-rb--compact .rb-c-fit .is-skip b{color:var(--mj-alert)}
.scms-rb.scms-rb--compact .rb-c-action{display:flex;flex-direction:column;align-items:flex-end;
  gap:8px;text-align:right}
.scms-rb--sidebar{position:sticky;top:96px;max-width:320px;
  border:1px solid var(--mj-gray-200);border-radius:var(--radius-lg);
  background:var(--mj-surface);overflow:hidden;
  box-shadow:0 4px 16px rgba(0,0,0,.04)}
body.dark-mode .scms-rb--sidebar{background:#161618;border-color:#2a2a2d;box-shadow:none}
.scms-rb--sidebar .rb-s-top{padding:var(--sp-5);border-bottom:1px solid var(--mj-gray-200);
  text-align:center}
body.dark-mode .scms-rb--sidebar .rb-s-top{border-color:#2a2a2d}
.scms-rb--sidebar .rb-s-visual{width:100%;height:120px;border-radius:var(--radius-md);
  background:linear-gradient(135deg,#f2f2f2,#e6e6e6);display:flex;align-items:center;
  justify-content:center;font-family:var(--font-meta);font-size:.66rem;letter-spacing:1px;
  text-transform:uppercase;color:var(--mj-gray-400);margin-bottom:var(--sp-4)}
body.dark-mode .scms-rb--sidebar .rb-s-visual{background:linear-gradient(135deg,#232326,#1a1a1c)}
.scms-rb--sidebar .scms-rb-heading{font-size:1.2rem;margin-bottom:6px}
.scms-rb--sidebar .rb-s-scorerow{display:flex;align-items:center;justify-content:center;
  gap:8px;margin-top:8px}
.scms-rb--sidebar .rb-s-pros{list-style:none;margin:0;padding:var(--sp-5);
  display:flex;flex-direction:column;gap:10px}
.scms-rb--sidebar .rb-s-pros li{display:flex;gap:9px;font-size:.9rem;line-height:1.45;
  align-items:flex-start}
.scms-rb--sidebar .rb-s-pros li::before{content:"";flex-shrink:0;width:16px;height:16px;
  margin-top:2px;border-radius:1px;background:var(--mj-growth-subtle);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%230b7a5b' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:center}
body.dark-mode .scms-rb--sidebar .rb-s-pros li::before{background-color:rgba(16,185,129,.18)}
.scms-rb--sidebar .rb-s-price{padding:0 var(--sp-5) var(--sp-4);
  font-family:var(--font-display);font-weight:800;font-size:1.35rem;color:var(--mj-ink)}
body.dark-mode .scms-rb--sidebar .rb-s-price{color:#f4f4f5}
.scms-rb--sidebar .rb-s-price small{display:block;font-family:var(--font-meta);font-weight:400;
  font-size:.72rem;letter-spacing:.5px;color:var(--mj-gray-400);margin-top:2px}
.scms-rb--sidebar .rb-s-foot{padding:0 var(--sp-5) var(--sp-5);display:flex;flex-direction:column;gap:8px}
.scms-rb--sidebar .rb-s-foot .scms-rb-cta{width:100%}
.scms-rb--full{max-width:760px;margin:var(--sp-6) auto;
  border:1px solid var(--mj-gray-200);border-top:4px solid var(--rb-accent);
  border-radius:var(--radius-md);background:var(--mj-surface);overflow:hidden}
body.dark-mode .scms-rb--full{background:#161618;border-color:#2a2a2d;border-top-color:var(--rb-accent)}
.scms-rb--full .rb-f-head{display:flex;gap:var(--sp-5);align-items:flex-start;
  padding:var(--sp-6);border-bottom:1px solid var(--mj-gray-200)}
body.dark-mode .scms-rb--full .rb-f-head{border-color:#2a2a2d}
.scms-rb--full .rb-f-visual{flex-shrink:0;width:120px;height:120px;border-radius:var(--radius-md);
  background:linear-gradient(135deg,#f2f2f2,#e6e6e6);display:flex;align-items:center;
  justify-content:center;font-family:var(--font-meta);font-size:.6rem;letter-spacing:1px;
  text-transform:uppercase;color:var(--mj-gray-400);text-align:center;padding:8px}
body.dark-mode .scms-rb--full .rb-f-visual{background:linear-gradient(135deg,#232326,#1a1a1c)}
.scms-rb--full .rb-f-visual.has-img{background:var(--mj-white);border:1px solid var(--mj-gray-200)}
body.dark-mode .scms-rb--full .rb-f-visual.has-img{background:#141414;border-color:#2a2a2d}
.scms-rb--full .rb-f-visual img{width:100%;height:100%;object-fit:contain;padding:8px;display:block}
.scms-rb--sidebar .rb-s-visual.has-img{background:var(--mj-white);border:1px solid var(--mj-gray-200)}
body.dark-mode .scms-rb--sidebar .rb-s-visual.has-img{background:#141414;border-color:#2a2a2d}
.scms-rb--sidebar .rb-s-visual img{width:100%;height:100%;object-fit:contain;padding:10px;display:block}
.scms-rb--full .rb-f-headmeta{flex:1;min-width:0}
.scms-rb--full .rb-f-headmeta .scms-rb-heading{font-size:1.6rem;margin:4px 0 6px}
.scms-rb--full .rb-f-bigscore{display:flex;align-items:center;gap:12px;margin-top:10px;flex-wrap:wrap}
.scms-rb--full .rb-f-bigscore .scms-rb-score b{font-size:2.4rem}
.scms-rb--full .rb-f-oneline{font-family:var(--font-body);font-size:1.05rem;line-height:1.5;
  color:var(--mj-charcoal);margin:8px 0 0}
body.dark-mode .scms-rb--full .rb-f-oneline{color:#c9c9cc}
.scms-rb--full .rb-f-scores{padding:var(--sp-6);border-bottom:1px solid var(--mj-gray-200)}
body.dark-mode .scms-rb--full .rb-f-scores{border-color:#2a2a2d}
.scms-rb--full .rb-f-proscons{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-6);
  padding:var(--sp-6);border-bottom:1px solid var(--mj-gray-200)}
body.dark-mode .scms-rb--full .rb-f-proscons{border-color:#2a2a2d}
.scms-rb--full .rb-f-fit{display:grid;grid-template-columns:1fr 1fr;gap:0;
  border-bottom:1px solid var(--mj-gray-200)}
body.dark-mode .scms-rb--full .rb-f-fit{border-color:#2a2a2d}
.rb-f-fit .cell .scms-rb-eyebrow{margin-bottom:6px}
.rb-f-fit .cell.is-for .scms-rb-eyebrow{color:#0b7a5b}
.rb-f-fit .cell.is-avoid .scms-rb-eyebrow{color:var(--mj-alert)}
.scms-rb--full .rb-f-verdict{padding:var(--sp-6);background:var(--mj-surface-2)}
body.dark-mode .scms-rb--full .rb-f-verdict{background:#101012}
.rb-f-verdict .scms-rb-eyebrow{margin-bottom:8px}
.rb-f-cta-row .scms-rb-checked{margin-left:auto}
@media (max-width:900px){
  .scms-rb--sidebar{position:static;max-width:var(--article-width);margin:var(--sp-5) auto}
}
@media (max-width:640px){
  .scms-rb.scms-rb--compact{grid-template-columns:1fr;gap:var(--sp-4)}
  .scms-rb.scms-rb--compact .rb-c-score{border-right:none;border-bottom:1px solid var(--mj-gray-200);
    padding-right:0;padding-bottom:var(--sp-4);display:flex;justify-content:center}
  .scms-rb.scms-rb--compact .rb-c-action{align-items:stretch;text-align:left}
  /* ── WHAT YOU PAY, IN A REVIEW BLOCK · observation 20.3 ──────────────────
       "improve the existing designs to always include the discounts/offers and
        the final offer price to be paid if someone ends up using our links …
        Variation 1 could have a lot of better negative space used up by adding
        some info in the panel where the buttons are."                — Lucky

   The action column carried a price, a spec line and a button, and a third of
   its height was empty. The gap is exactly the size of the one thing the panel
   was not saying: what the reader pays HERE versus what they pay on the
   vendor's site.

   THE SAME THREE FACTS AS THE PRODUCT BOX, IN A NARROW COLUMN. `.mj-pb__pay`
   lays them out as a row because a product box has width to spare; this panel
   does not, so they stack. One truth source — `offers.for_product` — two
   presentations. That is reuse of the FACT, which is the part that must never
   diverge; laying it out twice for two shapes is design, not duplication.

   IT RENDERS ONLY FROM A VERIFIED OFFER. With none in the ledger the panel keeps
   the plain price it has always shown, and nothing implies a discount exists. */

.scms-rb.scms-rb--compact .rb-c-action .scms-rb-cta{width:100%}
  .scms-rb--full .rb-f-head{flex-direction:column}
  .scms-rb--full .rb-f-proscons, .scms-rb--full .rb-f-fit{grid-template-columns:1fr}
  .scms-rb--full .rb-f-fit .cell.is-for{border-right:none;border-bottom:1px solid var(--mj-gray-200)}
}

/* --- CTA block
     (from mashjoy-cta-block.html) --- */
.scms-cta--inline{max-width:var(--article-width);margin:var(--sp-6) auto;padding:var(--sp-5) var(--sp-5);
  display:flex;align-items:center;gap:var(--sp-5);flex-wrap:wrap;
  background:var(--mj-surface-2);border-left:3px solid var(--link-action);border-radius:0 var(--radius-md) var(--radius-md) 0}
body.dark-mode .scms-cta--inline{background:#161618}
.scms-cta--inline .txt{flex:1;min-width:220px}
.scms-cta--inline .lead{font-family:var(--font-display);font-weight:800;font-size:1.08rem;line-height:1.25;
  color:var(--mj-black);margin:0 0 4px}
body.dark-mode .scms-cta--inline .lead{color:#fff}
.scms-cta--inline .sub{font-size:.95rem;line-height:1.5;color:var(--mj-text-muted);margin:0}
body.dark-mode .scms-cta--inline .sub{color:#b6b6bb}
.scms-cta--inline .act{display:flex;flex-direction:column;gap:4px;align-items:flex-start}
.scms-cta--banner{max-width:760px;margin:var(--sp-7) auto;background:var(--mj-ink-900);
  border-radius:var(--radius-lg);padding:var(--sp-7) var(--sp-7);color:#fff;
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-6);flex-wrap:wrap;
  border:1px solid #1e1e22;position:relative;overflow:hidden}
.scms-cta--banner::before{content:"";position:absolute;top:-40%;right:-10%;width:340px;height:340px;
  background:radial-gradient(circle,rgba(236,34,39,.28),transparent 70%);pointer-events:none}
.scms-cta--banner .txt{flex:1;min-width:260px;position:relative;z-index:1}
.scms-cta--banner .eyebrow{font-family:var(--font-meta);font-size:.66rem;letter-spacing:1.5px;
  text-transform:uppercase;color:var(--mj-red-subtle);margin:0 0 8px}
.scms-cta--banner h3{font-family:var(--font-display);font-weight:800;font-size:clamp(1.4rem,3vw,1.9rem);
  line-height:1.12;margin:0 0 10px;color:#fff;text-wrap:balance}
.scms-cta--banner p{font-size:1rem;line-height:1.55;color:#c7c7cc;margin:0;max-width:44ch}
.scms-cta--banner .act{position:relative;z-index:1;display:flex;flex-direction:column;gap:8px;align-items:flex-start}
/* `.scms-cta--banner p` sets 1rem and outranks a lone class, so the banner's
   disclosure rendered at READING SIZE — 16px and four lines directly under the
   button, where the inline variant of the same element is 10.5px and two. Lucky
   spotted it on "Trading by tomorrow". Matched at equal weight rather than
   reached for with !important. */
.scms-cta--banner p.cta-disc{ font-size:.66rem; line-height:1.45;
  letter-spacing:.2px }
.scms-cta--banner .cta-disc{color:#8a8a90}
.scms-cta--banner .cta-disc b{color:#c7c7cc}
.scms-cta--midfunnel{max-width:var(--article-width);margin:var(--sp-7) auto;background:var(--mj-surface);
  border:1px solid var(--mj-rule);border-top:4px solid var(--link-action);border-radius:var(--radius-lg);
  padding:var(--sp-6) var(--sp-6);box-shadow:0 2px 12px rgba(0,0,0,.04)}
body.dark-mode .scms-cta--midfunnel{background:#131315;border-color:#2a2a2d;border-top-color:var(--link-action);box-shadow:none}
.scms-cta--midfunnel .eyebrow{font-family:var(--font-meta);font-size:.66rem;letter-spacing:1.5px;
  text-transform:uppercase;color:var(--mj-red);margin:0 0 8px}
.scms-cta--midfunnel h3{font-family:var(--font-display);font-weight:800;font-size:1.5rem;line-height:1.15;
  color:var(--mj-black);margin:0 0 10px;text-wrap:balance}
body.dark-mode .scms-cta--midfunnel h3{color:#fff}
.scms-cta--midfunnel .intro{font-size:1rem;line-height:1.6;color:var(--mj-text-muted);margin:0 0 var(--sp-5)}
body.dark-mode .scms-cta--midfunnel .intro{color:#b6b6bb}
.scms-cta--midfunnel .routes{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-4)}
.scms-cta--midfunnel .route{border:1px solid var(--mj-rule);border-radius:var(--radius-md);padding:var(--sp-4);
  display:flex;flex-direction:column;gap:8px;background:var(--mj-surface-2)}
body.dark-mode .scms-cta--midfunnel .route{background:#17171a;border-color:#2a2a2d}
.scms-cta--midfunnel .route .rlabel{font-family:var(--font-meta);font-size:.62rem;letter-spacing:1px;
  text-transform:uppercase;color:var(--mj-growth-text)}
.scms-cta--midfunnel .route h4{font-family:var(--font-display);font-weight:800;font-size:1.12rem;margin:0;color:var(--mj-black)}
body.dark-mode .scms-cta--midfunnel .route h4{color:#fff}
.scms-cta--midfunnel .route .why{font-size:.9rem;line-height:1.5;color:var(--mj-text-muted);margin:0;flex:1}
body.dark-mode .scms-cta--midfunnel .route .why{color:#a7a7ad}
.scms-cta--midfunnel .route .cta-btn{width:100%;margin-top:4px}
.scms-cta--midfunnel .route.alt .cta-btn{background:transparent;color:var(--mj-black);border:1px solid var(--mj-gray-300)}
body.dark-mode .scms-cta--midfunnel .route.alt .cta-btn{color:#fff;border-color:#444}
.scms-cta--midfunnel .route.alt .cta-btn:hover{border-color:var(--link-action);color:var(--link-action)}
body.dark-mode .scms-cta--midfunnel .route.alt .cta-btn:hover{color:#ff6b6f;border-color:#ff6b6f}

/* --- FAQ accordion
     (from mashjoy-faq.html) --- */
.scms-faq{max-width:var(--article-width);margin:var(--sp-6) auto}
.scms-faq__head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-4);
  flex-wrap:wrap;margin:0 0 var(--sp-4)}
.scms-faq__title{font-family:var(--font-display);font-weight:800;letter-spacing:-.015em;
  font-size:clamp(1.5rem,3vw,2rem);color:var(--mj-black);margin:0}
body.dark-mode .scms-faq__title{color:#fff}
.scms-faq__expand{background:none;border:none;cursor:pointer;font-family:var(--font-meta);
  font-size:.68rem;letter-spacing:1px;text-transform:uppercase;color:var(--mj-trust);padding:4px 0}
.scms-faq__expand:hover{color:var(--mj-red)}
.scms-faq__expand:focus-visible{outline:2px solid var(--mj-trust);outline-offset:3px;border-radius:2px}
.scms-faq__item{border-bottom:1px solid var(--mj-rule)}
body.dark-mode .scms-faq__item{border-color:#242427}
.scms-faq__item:first-of-type{border-top:1px solid var(--mj-rule)}
body.dark-mode .scms-faq__item:first-of-type{border-color:#242427}
.scms-faq__q{width:100%;background:none;border:none;cursor:pointer;color:var(--mj-black);text-align:left;
  display:flex;justify-content:space-between;align-items:center;gap:16px;padding:18px 2px;
  font-family:var(--font-display);font-weight:700;font-size:1.08rem;line-height:1.32}
body.dark-mode .scms-faq__q{color:#f2f2f2}
.scms-faq__q:hover{color:var(--mj-red)}
.scms-faq__q:focus-visible{outline:2px solid var(--mj-trust);outline-offset:2px;border-radius:3px}
.scms-faq__q .ic{width:20px;height:20px;flex:none;position:relative}
.scms-faq__q .ic::before, .scms-faq__q .ic::after{content:"";position:absolute;background:currentColor;
  border-radius:2px;transition:transform .25s ease,opacity .25s ease}
.scms-faq__q .ic::before{top:9px;left:2px;width:16px;height:2.5px}
.scms-faq__q .ic::after{top:2px;left:8.75px;width:2.5px;height:16px}
.scms-faq__q[aria-expanded="true"] .ic::after{transform:rotate(90deg);opacity:0}
.scms-faq__a{overflow:hidden;max-height:0;transition:max-height .3s ease}
.scms-faq__item.is-open .scms-faq__a{max-height:480px}
.scms-faq__a-inner{padding:0 2px 20px}
.scms-faq__a p{font-size:1.02rem;line-height:1.65;color:var(--mj-text-muted);margin:0}
body.dark-mode .scms-faq__a p{color:#b6b6bb}
.scms-faq__a p+p{margin-top:12px}
.scms-faq__a a{color:var(--mj-trust);font-weight:600;border-bottom:1px solid transparent;transition:.15s}
.scms-faq__a a:hover{border-color:var(--mj-trust)}
.scms-faq__a a:focus-visible{outline:2px solid var(--mj-trust);outline-offset:2px;border-radius:2px}
.scms-faq__verified{font-family:var(--font-meta);font-size:.68rem;letter-spacing:.5px;color:var(--mj-gray-400);
  margin:var(--sp-4) 0 0;display:flex;gap:8px;align-items:center}
.scms-faq__verified svg{width:14px;height:14px;stroke:var(--mj-growth);stroke-width:1.9;fill:none;flex:none}

/* --- L0 pass 2: element CSS from the article prototypes' later <style> blocks --- */

/* --- list single-template inserts --- */
body.dark-mode .mj-pb__name{color:#fff}
.mj-pb__hook{font-size:1rem;color:var(--mj-text-muted);line-height:1.55;margin:10px 0 14px}
body.dark-mode .mj-pb__hook{color:#b6b6bb}
body.dark-mode .mj-pb__price{color:#fff}
body.dark-mode .mj-pb__specs li{border-color:#242427}
body.dark-mode .mj-pb__specs .k{color:#a7a7ad}
body.dark-mode .mj-pb__specs .v{color:#e7e7ea}
.mj-pb__badges{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.mj-pb__badge--editorial{background:var(--mj-trust-subtle);color:var(--mj-trust)}
body.dark-mode .mj-pb__badge--editorial{background:rgba(37,99,235,.18);color:#7fa8ff}
.mj-pb__fit{display:flex;flex-direction:column;gap:8px;margin:4px 0 16px}
.mj-pb__fit .row{display:flex;gap:10px;align-items:flex-start;font-size:.95rem;line-height:1.5;
  padding:10px 12px;border-radius:var(--radius-md)}
.mj-pb__fit .row b{font-family:var(--font-meta);text-transform:uppercase;letter-spacing:.5px;font-size:.64rem;
  white-space:nowrap;padding-top:3px}
.mj-pb__fit .row.for{background:var(--mj-growth-subtle)}
body.dark-mode .mj-pb__fit .row.for{background:rgba(16,185,129,.1)}
.mj-pb__fit .row.for b{color:#0b7a5b}
body.dark-mode .mj-pb__fit .row.for b{color:#34d8a6}
.mj-pb__fit .row.avoid{background:var(--mj-alert-subtle)}
body.dark-mode .mj-pb__fit .row.avoid{background:rgba(193,18,31,.12)}
.mj-pb__fit .row.avoid b{color:var(--mj-alert)}
body.dark-mode .mj-pb__fit .row.avoid b{color:#f08a8f}
.mj-pb__fit .row.alt{background:var(--mj-surface-2)}
body.dark-mode .mj-pb__fit .row.alt{background:#1a1a1d}
.mj-pb__fit .row.alt b{color:var(--mj-gray-400)}
.mj-pb__fit .row a{color:var(--mj-charcoal);font-weight:700;border-bottom:1px dotted var(--mj-charcoal)}
body.dark-mode .mj-pb__fit .row a{color:#e2e2e6;border-color:#666}
.mj-pb__fit .row a:hover{color:var(--mj-red);border-color:var(--mj-red)}
.mj-pb__actions{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
.mj-pb__cta--quote{background:var(--mj-navy)}
.mj-pb__cta--quote:hover{background:#0f1622;color:#fff}
.mj-ph{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;
  background:repeating-linear-gradient(45deg,var(--mj-surface-2),var(--mj-surface-2) 10px,transparent 10px,transparent 20px);
  color:var(--mj-gray-400);text-align:center;padding:14px}
body.dark-mode .mj-ph{background:repeating-linear-gradient(45deg,#1a1a1d,#1a1a1d 10px,transparent 10px,transparent 20px)}
.mj-ph svg{width:52px;height:52px;stroke:var(--mj-gray-300);stroke-width:1.4;fill:none}
body.dark-mode .mj-ph svg{stroke:#3a3a3f}
.mj-ph .lbl{font-family:var(--font-display);font-weight:800;font-size:.86rem;color:var(--mj-charcoal);line-height:1.15}
body.dark-mode .mj-ph .lbl{color:#cfcfd2}
.mj-ph .sub{font-family:var(--font-meta);font-size:.6rem;letter-spacing:.5px;text-transform:uppercase}
.mj-pb--rank{display:grid;grid-template-columns:220px 1fr;gap:28px;padding:28px;margin:var(--sp-6) auto;scroll-margin-top:120px}
/* EVERY OTHER PRODUCT-BOX VARIANT COLLAPSES ON A PHONE AND THIS ONE DID NOT
   (found 2026-08-27). `--pbx`, `--slim` and `-grid` each carry a breakpoint;
   `--rank` was missed, so at 375px the grid held 220px + 69px and the
   get-what panel inside the second column overflowed the viewport by 37px.
   A ranked box is the most-used element on the site's money pages. */
@media(max-width:720px){
  .mj-pb--rank{ grid-template-columns:minmax(0,1fr); gap:var(--sp-4); padding:var(--sp-5) }
  .mj-pb--rank .mj-pb__img{ max-width:220px }
}
.mj-pb--rank.is-pick{box-shadow:0 8px 24px rgba(16,185,129,.10)}
.mj-pb--rank .rank-col{display:flex;flex-direction:column;gap:12px}
.mj-pb--rank .rank-num{display:flex;align-items:center;gap:8px;font-family:var(--font-display);font-weight:800;
  font-size:1.05rem;color:var(--mj-black)}
body.dark-mode .mj-pb--rank .rank-num{color:#fff}
.mj-pb--rank .rank-num i{font-style:normal;width:34px;height:34px;border-radius:2px;background:var(--mj-ink-900);color:#fff;
  display:flex;align-items:center;justify-content:center;font-size:1rem;flex:none}
body.dark-mode .mj-pb--rank .rank-num i{background:#fff;color:#0a0a0a}
.mj-pb--rank.is-pick .rank-num i{background:var(--mj-growth)}
.mj-pb--rank .rank-num em{font-style:normal;font-family:var(--font-meta);font-size:.62rem;letter-spacing:1px;
  text-transform:uppercase;color:var(--mj-gray-400)}
.mj-pb--rank .mj-pb__name{font-size:1.5rem;margin-bottom:6px}
/* THE STAMP RIDES BESIDE THE NAME (Lucky, c-ul6r 2.1: "the CHECKED 23 AUG 2026
   element appears to the right of the name part so that we can gain a bit of
   height reduction"). It had a row of its own above the eyebrow — a full line
   spent on one chip. It wraps under the name only when the name is too long to
   share the line, so a long product name is never squeezed to make room. */
.mj-pb--rank .mj-pb__titlerow{display:flex;align-items:center;flex-wrap:wrap;gap:6px 9px;margin:0 0 6px}
.mj-pb--rank .mj-pb__titlerow .mj-pb__name{margin:0}
.mj-pb--rank .mj-pb__titlerow .mj-pb__badge--verified{flex:none;white-space:nowrap}
/* MEASURED, NOT GUESSED: the body is 371px and "WAPPoint PrintPOS" is 230px at
   1.5rem, so a 160px stamp wrapped under it and saved no height at all. At this
   size the stamp is ~118px and the pair fits. HelloPay's name is 303px on its
   own and still wraps, which is the rule working: the name is never shrunk. */
.mj-pb--rank .mj-pb__titlerow .mj-pb__badge--verified{font-size:.58rem;letter-spacing:.3px;padding:4px 7px;gap:4px}

/* --- comparison-2products single-template inserts --- */
.mj-cmp-wrap{max-width:var(--wide);margin:var(--sp-6) auto}
.mj-cmp-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;border-radius:var(--radius-lg)}
.mj-cmp{min-width:660px;width:100%;border-collapse:separate;border-spacing:0;background:var(--mj-surface);
  border:1px solid var(--mj-rule);border-radius:var(--radius-lg);overflow:hidden}
body.dark-mode .mj-cmp{background:#131315;border-color:#2a2a2a}
.mj-cmp th, .mj-cmp td{padding:15px 18px;text-align:left;border-bottom:1px solid var(--mj-rule);vertical-align:top}
body.dark-mode .mj-cmp th, body.dark-mode .mj-cmp td{border-color:#242427}
.mj-cmp tr:last-child td, .mj-cmp tr:last-child th{border-bottom:none}
.mj-cmp .row-label{font-family:var(--font-meta);font-size:.72rem;letter-spacing:.5px;text-transform:uppercase;
  color:var(--mj-text-muted);width:170px;white-space:nowrap;background:var(--mj-surface)}
body.dark-mode .mj-cmp .row-label{color:#a7a7ad;background:#131315}
.mj-cmp thead th{vertical-align:bottom}
.mj-cmp .col-head{min-width:230px;background:var(--mj-surface-2)}
body.dark-mode .mj-cmp .col-head{background:#1b1b1e}
.mj-cmp .col-head.is-winner{background:var(--mj-growth-subtle)}
body.dark-mode .mj-cmp .col-head.is-winner{background:rgba(16,185,129,.12)}
.mj-cmp .ch-img{width:64px;height:64px;border:1px solid var(--mj-rule);border-radius:var(--radius-md);
  background:#fff;display:flex;align-items:center;justify-content:center;overflow:hidden;margin-bottom:10px}
body.dark-mode .mj-cmp .ch-img{background:#0f0f10;border-color:#2a2a2a}
.mj-cmp .ch-img img{width:100%;height:100%;object-fit:contain;padding:6px}
.mj-cmp .ch-flag{display:inline-block;font-family:var(--font-meta);font-size:.6rem;letter-spacing:1px;
  text-transform:uppercase;font-weight:700;padding:3px 8px;border-radius:2px;margin-bottom:8px}
.mj-cmp .ch-flag.win{background:var(--mj-growth);color:#fff}
.mj-cmp .ch-flag.alt{background:var(--mj-trust-subtle);color:var(--mj-trust)}
body.dark-mode .mj-cmp .ch-flag.alt{background:rgba(37,99,235,.18);color:#7fa8ff}
.mj-cmp .ch-name{font-family:var(--font-display);font-weight:800;color:var(--mj-black);font-size:1.15rem;line-height:1.15}
body.dark-mode .mj-cmp .ch-name{color:#fff}
.mj-cmp .ch-hook{font-size:.82rem;color:var(--mj-text-muted);margin-top:4px}
.mj-cmp td .v{font-weight:700;color:var(--mj-black)}
body.dark-mode .mj-cmp td .v{color:#fff}
.mj-cmp td .sub{display:block;font-size:.8rem;color:var(--mj-gray-400);font-weight:400;margin-top:2px}
.mj-cmp .cell-win{color:#08694c;font-weight:800}
body.dark-mode .mj-cmp .cell-win{color:#34d8a6}
.mj-cmp .mj-pb__cta, .mj-cmp .mj-pb__cta--ghost{width:100%}
.mj-cmp-hint{font-family:var(--font-meta);font-size:.68rem;letter-spacing:.5px;color:var(--mj-gray-400);text-align:center;margin:8px 0 0}
.mj-chart{max-width:var(--wide);margin:var(--sp-6) auto;border:1px solid var(--mj-rule);
  border-radius:var(--radius-lg);background:var(--mj-surface);padding:var(--sp-6)}
body.dark-mode .mj-chart{background:#131315;border-color:#2a2a2a}
.mj-chart h4{font-family:var(--font-display);font-weight:800;font-size:1.15rem;color:var(--mj-black);margin:0 0 4px}
body.dark-mode .mj-chart h4{color:#fff}
.mj-chart .sub{font-family:var(--font-meta);font-size:.7rem;letter-spacing:1px;text-transform:uppercase;color:var(--mj-gray-400);margin:0 0 var(--sp-4)}
.mj-chart .legend{display:flex;gap:var(--sp-5);flex-wrap:wrap;margin:var(--sp-3) 0 0;font-size:.85rem}
.mj-chart .legend span{display:inline-flex;align-items:center;gap:8px;color:var(--mj-text-muted)}
body.dark-mode .mj-chart .legend span{color:#b6b6bb}
.mj-chart .legend i{width:22px;height:0;border-top:3px solid;display:inline-block}
.mj-chart svg{width:100%;height:auto;display:block;margin-top:var(--sp-3)}
.mj-chart .cwm{font-family:var(--font-meta);font-size:.66rem;letter-spacing:.5px;color:var(--mj-gray-400);margin:var(--sp-3) 0 0}

/* ── ARTICLE-TOP AFFILIATE DISCLOSURE · `.a-disclose` ─────────────────────
   Lucky asked for this back at the top of articles, and separately asked that
   the space stop being spent on boilerplate:

     "People already know what an affiliate link means … from there you can use
      that section to explain some of the benefits they will get if they use the
      link. People are more prone to buy if they are told the benefits than to
      be told this is an affiliate link."

   So it still says the thing it is legally and editorially required to say —
   first, plainly, in bold — and then spends its remaining words on the reader
   rather than on us: what using the link is worth to them, and what it does not
   buy (a ranking). "Costs you nothing extra" is a fact about them; "we may earn
   a commission" is a fact about us, and only one of those is worth the reader's
   attention at the top of an article they have not decided to trust yet. */
/* `.mj-article > p` sets prose at 1.06rem and outranks a lone class, so this
   notice rendered at reading size — four lines of grey where two were intended.
   Matched at the same weight, later in the file, rather than reached for with
   !important. */
.mj-article > p.a-disclose,
.a-disclose{ display:flex; align-items:flex-start; gap:10px;
  max-width:var(--article-width); margin:0 auto var(--sp-5);
  padding:var(--sp-4) var(--sp-4); background:var(--mj-surface-2);
  border-left:3px solid var(--mj-growth-text);
  font-size:.8rem; line-height:1.55; color:var(--mj-text-muted) }
.mj-article > p.a-disclose{ font-size:.8rem; line-height:1.55 }
@media(max-width:720px){ .a-disclose{ max-width:100%; padding-left:5%; padding-right:5% } }
body.dark-mode .a-disclose{ background:#141416; color:#b6b6bb }
.a-disclose svg{ width:17px; height:17px; flex:0 0 auto; margin-top:2px;
  stroke:var(--mj-growth-text); stroke-width:1.8; fill:none }
.a-disclose b{ color:var(--mj-black) }
body.dark-mode .a-disclose b{ color:#f0f0f2 }

/* ── THE ONE-LINE OFFER · `.mj-offer` ─────────────────────────────────────
   Lucky, 2026-08-22: "add the discounts and offers and what they get if they
   sign up with the link in these elements … but you must do it in a strategic
   way so that these elements don't always appear to be the same. I also
   understand that you can't just fit all the information in all the elements."

   So the offer layer has THREE weights, and the element's size chooses:

     `.mj-pb__pay`   the 3-column grid — vendor price, your price, the tag.
                     Only where there is room: the ranked card, the hero.
     `.mj-getwhat`   the ticked benefit list. Same places, plus the review block.
     `.mj-offer`     THIS — one line, no grid, no list. For everything small:
                     inline CTAs, banners, stacked cards, placements.

   One line still has to carry the whole argument, so it says all three things
   in the order a buyer asks them: what it normally costs (struck), what you pay
   (loud), and what that is worth (a green tag, because a saving is a gain and
   green is already this desk's colour for a fact in the reader's favour). Red
   is reserved for the action; a red discount and a red button in the same block
   are two things shouting the same volume at different jobs. */
/* CENTRE, NOT BASELINE. Lucky: "there is a bit of an alignment issue where
   things just don't feel like they are well aligned … align them horizontally
   so that they can all belong to the same line."

   `baseline` is the right instinct for three pieces of text and the wrong one
   here, because one of the three is not text: the tag is a BOX, and a box's
   baseline is the text inside its own padding and border. So the chip sat 2.5px
   below the price it belongs to — small enough that nobody can name it and
   large enough that the row reads as slightly broken. Centring aligns the
   things themselves rather than the letters inside them. */
.mj-offer{ display:inline-flex; align-items:center; flex-wrap:wrap; gap:8px;
  font-family:var(--font-body); line-height:1.3 }
.mj-offer__was{ font-family:var(--font-meta); font-size:.8rem;
  letter-spacing:.3px; color:var(--mj-gray-400);
  text-decoration:line-through; font-variant-numeric:tabular-nums }
.mj-offer__now{ font-family:var(--font-display); font-weight:800; font-size:1.06rem;
  color:var(--mj-black); font-variant-numeric:tabular-nums }
body.dark-mode .mj-offer__now{ color:#fff }
/* ── THE DEAL TAG · ONE DESIGN, FOUR CALL SITES ───────────────────────────
   Lucky, 2026-08-22:
     "All not round, but square with our mashjoy.com small rounding — the large
      radius doesn't feel like a mashjoy.com element in this application …
      they should all be the same in design (fewer variations are preferred for
      design taste) … consider the use of a mix of fonts."

   THREE THINGS WERE WRONG AND THE THIRD WAS THE WORST.

   1. ROUND. `--radius-sm` on this site is ONE PIXEL. The library is square-first
      by directive (G1), and a 999px pill is not a smaller version of that
      language, it is a different one. Round survives elsewhere because a
      quiz option IS a pill; a price tag is a label, and labels here are square.

   2. FOUR DESIGNS FOR ONE FACT. `.mj-offer__tag`, `.mj-pb__pay-tag`,
      `.scms-rb-pay__tag` and `.mj-pb__badge--deal` all say "R100 off" and each
      was styled on its own — and `--deal` said it in the ALERT colour, so the
      identical saving rendered green in a CTA and red in a slim row. A reader
      moving down the page met the same fact in two colours and had no way to
      know it was the same fact. One rule now; four selectors pointing at it.

   3. THE FONT MIX IS THE TASTE. Technor carries the NUMBER, because a number is
      the thing being asserted and Technor is this desk's assertive face. Canva
      Sans carries the LABEL around it, because a label is scaffolding. The
      struck price is meta too — it is a reference, not a claim. That split is
      what stops the line reading as one undifferentiated blur of price. */
.mj-offer__tag,
.mj-pb__pay-tag,
.scms-rb-pay__tag,
.mj-pb__badge--deal{ display:inline-flex; align-items:center; gap:6px;
  font-family:var(--font-meta); font-size:.6rem; font-weight:700;
  letter-spacing:.6px; text-transform:uppercase; white-space:nowrap;
  color:var(--mj-growth-text); background:var(--mj-growth-subtle);
  border:1px solid rgba(5,122,83,.24); border-radius:var(--radius-sm);
  padding:3px 7px; flex:0 0 auto }
/* THE MARKER, NOT A DOT — directive G6. The square is the brand's own mark, and
   it is what makes this read as a mashjoy.com label rather than a generic chip. */
/* A PLUS, NOT A BULLET. Lucky: "instead of using that square bullet in front of
   this text, you can just make it be a plus so that it feels like continuous."

   He is describing what the two marks MEAN. A square is a bullet — it says
   "here is an item in a list", which is what the marker does everywhere else on
   this site. But this chip is not an item, it is an ADDITION: the price above it
   already stands, and this is what comes off on top of it. A plus says that in
   one character, and it reads as continuous with the number beside it rather
   than as a new list starting. */
.mj-offer__tag::before,
.mj-pb__pay-tag::before,
.scms-rb-pay__tag::before,
.mj-pb__badge--deal::before{ content:"+"; width:auto; height:auto; flex:0 0 auto;
  font-family:var(--font-display); font-weight:800; font-size:.82rem;
  line-height:1; color:var(--mj-growth-text); background:none;
  margin-right:-1px }

/* THE MONEY IS THE CLAIM, so it carries the assertive face — same rule as the
   price beside it. "R100" in Technor inside a Canva Sans label is the same
   split that stops the price row reading as one blur: the face tells you which
   word is the fact and which words are scaffolding. */
.mj-offer__amt{ font-family:var(--font-display); font-weight:800;
  font-size:.72rem; letter-spacing:.4px; color:inherit }
body.dark-mode .mj-offer__tag,
body.dark-mode .mj-pb__pay-tag,
body.dark-mode .scms-rb-pay__tag,
body.dark-mode .mj-pb__badge--deal{ background:rgba(5,122,83,.16);
  border-color:rgba(5,122,83,.34) }
/* AN EMPTY PART IS NOT A PART. A brand with no verified discount renders the
   struck-price span empty, and an empty flex child still consumes a gap — so
   the price sat 8px right of where it should, for no visible reason. */
.mj-offer__was:empty,
.mj-offer__now:empty,
.mj-offer__tag:empty{ display:none }
/* ── URGENCY, AND WHAT IT IS ALLOWED TO SAY ───────────────────────────────
   Lucky asked for FOMO on these elements. The field group he pointed at has the
   right fields for it — `offer_expiry`, `urgency_score` ("No. of Times Used"),
   `special_offer_note` — so the CPT anticipated this properly.

   THIS LINE ONLY EVER STATES A FACT, and there are exactly three it can state:

     1. EXCLUSIVITY — "only through our link, not on yoco.com". This is TRUE for
        a referral discount and it is the strongest honest urgency available: the
        reader cannot get this price by going to the vendor directly, which is
        precisely the thing we want them to understand.
     2. A REAL EXPIRY — from `expires_on` in the ledger, which the offers layer
        already refuses to load past. If a vendor states an end date we say it.
     3. A REAL COUNT — `times_used`, from the affiliate performance ledger. It
        renders when there is a number and stays silent when there is not.

   It does NOT invent a countdown and it does NOT seed a counter. See §11.4 and
   the note in offers.py — the reason is not squeamishness, it is that this line
   sits two centimetres from "checked 25 July 2026", and a reader who catches one
   invented number stops believing the dated one too. */
.mj-offer__urg{ flex-basis:100%; display:flex; align-items:center; gap:6px;
  font-family:var(--font-meta); font-size:.66rem; letter-spacing:.2px;
  line-height:1.45; color:var(--mj-growth-text); margin-top:2px }
.mj-offer__urg:empty{ display:none }
.mj-offer__urg b{ font-family:var(--font-display); font-weight:800;
  font-size:.7rem; letter-spacing:.3px }
.scms-cta--banner .mj-offer__urg,
.mj-bnr .mj-offer__urg{ color:rgba(255,255,255,.82) }
.mj-finder__result[data-state="answered"] .mj-offer__urg{ color:rgba(255,255,255,.88) }
.mj-offer__note{ flex-basis:100%; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:.2px; color:var(--mj-gray-400) }
/* THE HOST COLLAPSES WHEN THERE IS NO OFFER. `.cta-offer` is a slot container,
   not a design decision: it exists so the renderer can compose an `offer_line`
   into a CTA without the renderer writing the offer's own markup. With no
   verified offer the slot renders empty and the container disappears, which is
   the only correct default — an empty offer box is worse than no offer box. */
/* Lucky: "the space between the prices and the button seems to be a little bit
   too big compared to other elements." It was — the offer sat on --sp-4 while
   every other stacked pair in these blocks sits on --sp-3. */
.cta-offer{ display:block; margin:0 0 var(--sp-2) }
.cta-offer:empty{ display:none }

/* ON A DARK OR RED GROUND the same line has to invert, because these placements
   sit inside banners as often as inside cards. */
.scms-cta--banner .mj-offer__now,
.mj-bnr .mj-offer__now{ color:#fff }
.scms-cta--banner .mj-offer__was,
.mj-bnr .mj-offer__was{ color:rgba(255,255,255,.55) }
/* THE TAG DOES NOT INVERT, and that is the point. It used to go white-on-
   translucent over a dark banner while its marker stayed green — one element
   wearing two liveries and neither of them consistent with the same tag on the
   card above it. The mint chip is already high-contrast on black (#E3FAF2 on
   #0A0A0A), so the saving looks identical wherever it appears, which is what
   makes it recognisable at a glance rather than re-read at every element.
   Only the PLAIN TEXT beside it inverts, because grey-on-black is unreadable
   and a mint chip on black is not. */
.scms-cta--banner .mj-offer__note,
.mj-bnr .mj-offer__note{ color:rgba(255,255,255,.6) }

/* ── WHAT YOU GET · `.mj-getwhat` ─────────────────────────────────────────
   Lucky, 2026-08-22, on every priced element on the site:

     "It would have been nice to include how much someone is going to end up
      paying after the R100 offer, as well as telling them the money lands in
      two days and it will already be set up … make it so they don't feel they
      should still go to the vendor website to verify prices."

   `.mj-pb__pay` already answers WHAT IT COSTS — vendor price, your price, the
   saving in whichever form reads biggest. This answers WHAT HAPPENS NEXT, and
   that is the half that decides whether the reader clicks our link or opens the
   vendor's site in a second tab. A reader who opens that tab does not come back
   through us, so the honest way to keep them is to have already told them the
   things they were going to go and look up.

   IT IS SMALL, AND ALL OF IT IS SMALL. Lucky: "if the font is to be small,
   everything should be small, including the techno font." So the label is the
   display face at the same size as the body lines, not a size larger — the
   emphasis comes from the face and the colour, never from scale. Green,
   because green is already this desk's colour for a verified fact, and every
   line here comes from `what_you_get` in the ledger with a date on it. */
.mj-getwhat{ margin:var(--sp-4) 0 0; padding:var(--sp-4) var(--sp-4) var(--sp-3);
  background:var(--mj-growth-tint,rgba(5,122,83,.05));
  border:1px solid rgba(5,122,83,.18); border-radius:var(--radius-sm) }
body.dark-mode .mj-getwhat{ background:rgba(5,122,83,.10);
  border-color:rgba(5,122,83,.28) }
.mj-getwhat__k{ font-family:var(--font-display); font-weight:700; font-size:.68rem;
  letter-spacing:.7px; text-transform:uppercase; color:var(--mj-growth-text);
  margin:0 0 7px }
/* THE MONEY IN A BENEFIT LINE, IN THE PRICE FACE. Lucky, 2026-08-26: bold the
   discounts and percentages so a scanned list gives up its number first. Technor
   because that is already the price face everywhere else on this site — the fee
   scale, the pay block, the rank figures — so a reader meets one typeface for
   money and one for prose, consistently. */
.mj-getwhat__fig{ font-family:var(--font-display); font-weight:800;
  font-size:1.02em; letter-spacing:-.01em; color:var(--mj-growth-text) }
body.dark-mode .mj-getwhat__fig{ color:#34d8a6 }

.mj-getwhat__l{ list-style:none; margin:0; padding:0; display:grid; gap:5px }
.mj-getwhat__i{ position:relative; padding-left:17px; font-size:.8rem;
  line-height:1.45; color:var(--mj-charcoal) }
body.dark-mode .mj-getwhat__i{ color:#cfcfd2 }
/* THE TOGGLE UNDER THE EXCLUSIVE LIST — Lucky, 2026-08-23.
   Standard vendor benefits are true and worth having, but most readers already
   know them and some have just come from the vendor's own site. Showing them
   under "what you get through our link" made the link look worthless. They live
   here instead: closed by default, quiet, and one click for the reader who has
   not done that homework. No arrow of our own — the disclosure marker is the
   browser's, which is the one readers already recognise. */
.mj-getwhat__more{ margin:8px 0 0 }
.mj-getwhat__sum{ cursor:pointer; list-style-position:outside }
.mj-getwhat__sum:hover{ color:var(--mj-charcoal) }
body.dark-mode .mj-getwhat__sum:hover{ color:#e9e9ec }
.mj-getwhat__std{ margin:6px 0 0; padding:0; list-style:none }
.mj-getwhat__si{ position:relative; padding-left:16px; margin:3px 0;
  font-size:.82rem; line-height:1.5; color:var(--mj-text-muted) }
.mj-getwhat__si::before{ content:"·"; position:absolute; left:4px; top:-.06em;
  color:var(--mj-gray-400); font-weight:700 }
body.dark-mode .mj-getwhat__si{ color:#a8a8ae }
.mj-getwhat__i::before{ content:"✓"; position:absolute; left:0; top:0;
  font-size:.74rem; font-weight:700; color:var(--mj-growth-text) }
.mj-getwhat__v{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:.2px;
  color:var(--mj-gray-400); margin:7px 0 0 }

/* ── TWO MORE PLACES A FINDER CAN LIVE ────────────────────────────────────
   Lucky, 2026-08-22: "build two additional variations ... with more versions
   that will end up fitting inside the content, or that can be embedded in
   places like when some inserts are used."

   ONE BEHAVIOUR, THREE PRESENTATIONS. Neither of these is a new element: both
   run the same inserts.js finder, the same weights, the same outcome model and
   the same tie rule. What changes is how much room the block asks for, which is
   a presentational decision and therefore travels as `data-layout` rather than
   as a second template. The alternative — three near-identical templates — is
   how a library starts disagreeing with itself.

   `nudge` · THE ONE-QUESTION NUDGE, for mid-content.
   Between two paragraphs, a reader has not asked for a decision panel. This is
   one question and a one-line answer, about 150px, on a hairline rather than a
   card: enough to route someone who already knows what they want, quiet enough
   to scroll past if they do not. The verdict-weight finder stays in verdicts.

   `embed` · THE HOSTED PICKER, for inside another insert.
   No border, no ground, no outer margin — it inherits whatever it is nested in,
   so it can sit inside a reveal panel, a tray or a comparison footer without
   the three-nested-boxes noise the inline reveal had. It is the same element
   with its packaging removed. */

/* — nudge — */
/* WHITE, AND IT STARTS WITH A BAR (Lucky, 2026-09-16).
   Transparent meant "whatever is behind me", which on a tinted host made the
   questions look like part of the host's furniture, and the contents sat hard
   against the text column because a thing with no ground needs no padding. A
   white ground with the desk's red rule across the top says where the element
   begins — the same way a review block announces itself — and the padding is
   the element's own, not the page's. */
.mj-finder[data-layout="nudge"]{ border:1px solid var(--mj-rule);
  border-top:3px solid var(--mj-red); border-radius:var(--radius-sm);
  background:#fff; margin:var(--sp-5) auto; padding:0 }
body.dark-mode .mj-finder[data-layout="nudge"]{ background:#161618;
  border-color:#2a2a2d; border-top-color:var(--mj-red) }
.mj-finder[data-layout="nudge"] .mj-finder__head{ padding:var(--sp-4) var(--sp-5) var(--sp-3) }
.mj-finder[data-layout="nudge"] .mj-finder__qs{ padding:0 var(--sp-5) }
/* THE ANSWER BAND RUNS TO THE BOX EDGES (Lucky, 2026-09-26, public liability):
   an inset band read as a second box inside the first. It is the box's floor. */
.mj-finder[data-layout="nudge"] .mj-finder__result{ margin:var(--sp-2) 0 0 }
.mj-finder[data-layout="nudge"] .mj-finder__h{ font-size:1.02rem }
.mj-finder[data-layout="nudge"] .mj-finder__sub{ display:none }
.mj-finder[data-layout="nudge"] .mj-finder__q{ border:0; padding:0 0 var(--sp-4) }
/* THE NUDGE'S ANSWER IS STILL A CTA. Lucky, on the first version of this:
   "make that entire section stand out like a CTA section … it should have the
   background of red instead of the typical grey or any of the dull colours."

   The first nudge left its answer on the page ground with a red bar down the
   side — quiet enough to be mistaken for a pull-quote. It keeps the nudge's
   smaller footprint (no kicker, no trade-off line, no note) but it is a
   PAINTED BAND like every other answer on the site, and it takes the same
   black→red state change. A recommendation that does not look like one is not
   being modest, it is being missed. */
.mj-finder[data-layout="nudge"] .mj-finder__result{ border-top:0;
  border-radius:0 0 var(--radius-sm) var(--radius-sm); padding:var(--sp-4) var(--sp-5) var(--sp-5) }
.mj-finder[data-layout="nudge"] .mj-finder__rk{ display:none }
.mj-finder[data-layout="nudge"] .mj-finder__rh{ font-size:1.06rem }
.mj-finder[data-layout="nudge"] .mj-finder__rt{ display:none }
.mj-finder[data-layout="nudge"] .mj-finder__note{ display:none }

/* — compact — (Lucky, c-3d4n, 2026-09-17)
   "An interactive section that starts off being minimal, not like a full standard
   interactive element, such that if they start answering, then it will expand to also
   ask them other questions up until it gives them a recommendation." Placed where a list
   begins, for the reader who has read enough and wants to know which one is theirs.
   CLOSED it is one line — the question and a round arrow. OPEN, the questions arrive one
   at a time (the finder already paces them) and the answer appears after the first reply,
   trade-off included. The collapsed state is set by inserts.js, so with JavaScript off
   the whole element shows and the recommendation is never gated (§11.8). */
.mj-finder[data-layout="compact"]{ border:1px solid var(--mj-rule);
  border-top:3px solid var(--mj-red); border-radius:var(--radius-sm);
  background:#fff; margin:var(--sp-5) auto; padding:0 }
body.dark-mode .mj-finder[data-layout="compact"]{ background:#161618;
  border-color:#2a2a2d; border-top-color:var(--mj-red) }
.mj-finder[data-layout="compact"] .mj-finder__head{ padding:var(--sp-4) var(--sp-5) var(--sp-3) }
.mj-finder[data-layout="compact"] .mj-finder__qs{ padding:0 var(--sp-5) }
.mj-finder[data-layout="compact"] .mj-finder__h{ font-size:1.02rem }
.mj-finder[data-layout="compact"] .mj-finder__q{ border:0; padding:0 0 var(--sp-4) }
.mj-finder[data-layout="compact"] .mj-finder__result{ margin:var(--sp-2) 0 0; border-top:0;
  border-radius:0 0 var(--radius-sm) var(--radius-sm); padding:var(--sp-4) var(--sp-5) var(--sp-5) }
.mj-finder[data-layout="compact"] .mj-finder__note{ display:none }
.mj-finder[data-layout="compact"][data-open="no"] .mj-finder__qs,
.mj-finder[data-layout="compact"][data-open="no"] .mj-finder__result,
.mj-finder[data-layout="compact"][data-open="no"] .mj-finder__sub{ display:none }
.mj-finder[data-layout="compact"][data-open="yes"] .mj-finder__result:not([data-state="answered"]){ display:none }
.mj-finder[data-layout="compact"][data-open="no"] .mj-finder__head{ position:relative; cursor:pointer;
  padding:var(--sp-4) 72px var(--sp-4) var(--sp-5); transition:background-color .15s ease }
.mj-finder[data-layout="compact"][data-open="no"] .mj-finder__head:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-finder[data-layout="compact"][data-open="no"] .mj-finder__head:hover{ background:#1d1d20 }
.mj-finder[data-layout="compact"][data-open="no"] .mj-finder__head::after{ content:""; position:absolute;
  right:var(--sp-5); top:50%; width:34px; height:34px; margin-top:-17px; border-radius:50%;
  background:var(--mj-red) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 6l6 6-6 6'/%3E%3C/svg%3E") center/16px no-repeat; transition:transform .15s ease }
.mj-finder[data-layout="compact"][data-open="no"] .mj-finder__head:hover::after{ transform:translateX(3px) }
.mj-finder[data-layout="compact"][data-open="no"] .mj-finder__head:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:-2px }
.mj-finder[data-tone="trust"][data-layout="compact"][data-open="no"] .mj-finder__head::after{ background-color:var(--mj-trust) }
.mj-finder[data-tone="growth"][data-layout="compact"][data-open="no"] .mj-finder__head::after{ background-color:var(--mj-growth-text) }

/* — embed — */
/* THE HOSTED ONE CARRIES ITS OWN SEPARATION (Lucky, 2026-09-16).
   "In cases where you have the interactive CTA element underneath the buttons,
   use the one that starts with a bar so that there is separation between the
   text and the actual element. It should be easy to see the actual CTA element
   and make it stand out." Inheriting the host's ground made it read as more of
   the host's prose; the bar is where the host stops and the decision starts.
   White, so it is the same element wherever it is hosted. */
.mj-finder[data-layout="embed"]{ border:0; border-top:3px solid var(--mj-red);
  border-radius:0; background:#fff; margin:var(--sp-4) 0 0; max-width:none;
  padding:var(--sp-4) var(--sp-5) var(--sp-5) }
body.dark-mode .mj-finder[data-layout="embed"]{ background:#161618 }
.mj-finder[data-layout="embed"] .mj-finder__head{ padding:0 0 var(--sp-3) }
.mj-finder[data-layout="embed"] .mj-finder__k{ display:none }
.mj-finder[data-layout="embed"] .mj-finder__h{ font-size:1rem }
.mj-finder[data-layout="embed"] .mj-finder__q{ border-top:0; padding:0 0 var(--sp-3) }
.mj-finder[data-layout="embed"] .mj-finder__result{ padding:var(--sp-4);
  border-radius:var(--radius-sm); border-top:0 }
.mj-finder[data-layout="embed"] .mj-finder__note{ display:none }

/* ── THE CONVERSION TRAY · `.mj-ctatray` ──────────────────────────────────
   Lucky, 2026-08-22, as a system rule rather than an Adumo fix:

     "If the user clicks a CTA indicating that they want to be contacted, the
      lead form must come to the user — the user must never have to go looking
      for the form. The conversion should remain inside the component where the
      conversion decision was made."

   The first build sent every lead CTA to ONE form at the foot of the article.
   That is the wrong shape twice over: the reader loses the context of what they
   just chose, and a general article ends with a standalone supplier form bolted
   under the content, which reads as an advert rather than an answer.

   A TRIGGER FINDS ITS TRAY BY PROXIMITY, NOT BY ID. inserts.js walks up from
   the button to the nearest `[data-lead-host]` and expands the tray inside THAT
   component. That single rule is what makes this reusable — a product box, a
   review block, a comparison row or a recommendation panel becomes a
   lead-capture host by carrying the attribute, with no new behaviour written
   for any of them.

   AND THE STANDALONE FORM SURVIVES. If a trigger has no host — a supplier-focused
   article where a dedicated form is the point — the click falls through to the
   page-level form, which is still a real in-page anchor with JavaScript off. */
.mj-ctatray{ border-top:3px solid var(--mj-ink-900); background:#fff }
body.dark-mode .mj-ctatray{ background:#161618; border-top-color:#fff }
.mj-ctatray[hidden]{ display:none }
.mj-ctatray[data-open]{ animation:mj-reveal-in .28s ease both }
/* The form inside a tray has already been introduced by the CTA above it, so it
   drops its own card chrome and inherits the component it was opened in. */
.mj-ctatray .mj-lead{ margin:0; border:0; border-radius:0; box-shadow:none;
  max-width:none }
.mj-ctatray .mj-lead[data-tray]{ display:block }

/* ── LEAD CAPTURE · `.mj-lead` ────────────────────────────────────────────
   THE THIRD CONVERSATIONAL CTA, and the one that unlocks a whole class of
   brand. Lucky, 2026-08-22:

     "Also have something that is able to capture the lead forms, because other
      suppliers will give us a lead form instead of capturing the affiliate
      pages through the links. Like, for example, Adumo."

   Adumo takes a referral form and calls the merchant back. Until now that meant
   a machine could win a route in an article and have nowhere for the reader to
   go — the finder on the seven-best list routes across four brands and had to
   leave Adumo out entirely, because an outcome with no action is not an
   outcome. This is the missing destination.

   SAME FAMILY, SAME RULES. White card so it lifts off the page, red action band
   so it reads as a decision, one button that pops. It is the finder's answer
   panel with fields where the affiliate buttons would be.

   IT ASKS FOR A REAL PERSON'S DETAILS, so the design carries obligations the
   other elements do not:
     · the destination is NAMED, in the consent line, above the button
     · consent is a CHECKBOX, not an assumption buried in small print
     · it asks for four fields, not Adumo's nine — a form that asks for
       everything the vendor's own portal asks for is a form nobody finishes
     · the commercial relationship is disclosed under the button like every
       other action on the site */
/* CLOSED ONLY WHEN THE SCRIPT IS THERE TO REOPEN IT. The attribute is set by
   inserts.js on load, never by the server — so a reader with JavaScript off
   gets a visible form and a working in-page anchor, and loses the animation
   rather than the ability to reach the partner. */
.mj-lead[data-tray="closed"]{ display:none }
.mj-lead[data-tray="open"]{ animation:mj-reveal-in .28s ease both }
.mj-lead{ display:block; max-width:var(--article-width); margin:var(--sp-6) auto;
  border:1px solid var(--mj-rule); border-top:3px solid var(--mj-red);
  border-radius:var(--radius-md); background:#fff; overflow:hidden;
  box-shadow:0 1px 2px rgba(10,10,10,.04), 0 8px 24px -12px rgba(10,10,10,.10) }
body.dark-mode .mj-lead{ background:#161618; border-color:#2a2a2d; box-shadow:none }

.mj-lead__head{ padding:var(--sp-5); background:linear-gradient(180deg,
  rgba(236,34,39,.045), rgba(236,34,39,0)) }
body.dark-mode .mj-lead__head{ background:linear-gradient(180deg,
  rgba(236,34,39,.09), rgba(236,34,39,0)) }
.mj-lead__k{ display:inline-flex; align-items:center; gap:7px;
  font-family:var(--font-display); font-weight:700; font-size:.62rem;
  letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-red);
  background:rgba(236,34,39,.08); border:1px solid rgba(236,34,39,.22);
  border-radius:999px; padding:5px 11px 5px 9px; margin:0 0 9px }
.mj-lead__k::before{ content:""; width:7px; height:7px; flex:0 0 auto;
  background:var(--mj-red) }
.mj-lead__h{ font-family:var(--font-display); font-weight:800; font-size:1.24rem;
  line-height:1.25; color:var(--mj-black); margin:0 0 6px }
body.dark-mode .mj-lead__h{ color:#fff }
.mj-lead__sub{ font-size:.92rem; line-height:1.6; color:var(--mj-text-muted); margin:0 }
body.dark-mode .mj-lead__sub{ color:#b6b6bb }

.mj-lead__fields{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--sp-4); padding:var(--sp-5); border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-lead__fields{ border-color:#242427 }
.mj-lead__f{ display:flex; flex-direction:column; gap:5px; min-width:0 }
.mj-lead__lbl{ font-family:var(--font-meta); font-size:.68rem; letter-spacing:.8px;
  text-transform:uppercase; color:var(--mj-text-muted) }
.mj-lead__in{ font-family:var(--font-body); font-size:.94rem; color:var(--mj-charcoal);
  padding:11px 13px; border:1px solid var(--mj-gray-300);
  border-radius:var(--radius-sm); background:#fff; width:100%;
  transition:border-color .15s ease }
.mj-lead__in:focus{ outline:none; border-color:var(--mj-red);
  box-shadow:0 0 0 3px rgba(236,34,39,.12) }
body.dark-mode .mj-lead__in{ background:#0f0f11; color:#e7e7ea; border-color:#3a3a3d }

/* THE ACTION BAND IS RED, like every other answer in this family. */
.mj-lead__act{ padding:var(--sp-5); background:var(--mj-red) }
.mj-lead__consent{ display:flex; align-items:flex-start; gap:10px;
  font-size:.84rem; line-height:1.5; color:#fff; margin:0 0 var(--sp-4); cursor:pointer }
.mj-lead__consent b{ color:#fff }
.mj-lead__check{ width:17px; height:17px; flex:0 0 auto; margin-top:2px;
  accent-color:#fff; cursor:pointer }
.mj-lead__cwrap{ display:block; min-width:0 }
/* The destination sentence is DATA — it names the brand receiving the details,
   so it changes per form. The privacy note under it is FURNITURE and must not:
   what we do with a reader's phone number is one promise, made the same way
   everywhere, not a sentence a writer gets to reword per article. */
.mj-lead__ctext{ display:block }
.mj-lead__cnote{ display:block; margin-top:4px; font-size:.76rem;
  color:rgba(255,255,255,.78) }
/* WHITE ON RED is the strongest button on the site, and this is the moment for
   it: the reader is handing over their phone number. */
.mj-lead__btn{ display:inline-flex; align-items:center;
  font-family:var(--font-display); font-size:.78rem; font-weight:600;
  letter-spacing:.6px; text-transform:uppercase; cursor:pointer;
  color:var(--mj-ink-900); background:#fff; border:1px solid #fff;
  padding:12px var(--sp-5); border-radius:var(--radius-sm);
  transition:background .15s ease, color .15s ease }
.mj-lead__btn:hover{ background:var(--mj-ink-900); color:#fff;
  border-color:var(--mj-ink-900) }
.mj-lead__btn:focus-visible{ outline:2px solid #fff; outline-offset:3px }
.mj-lead__note{ font-family:var(--font-meta); font-size:.66rem; letter-spacing:.2px;
  line-height:1.45; color:rgba(255,255,255,.72); margin:var(--sp-4) 0 0 }

@media(max-width:520px){
  .mj-lead__fields{ grid-template-columns:1fr }
  .mj-lead__btn{ width:100%; justify-content:center }
}

/* ── CONVERSATIONAL FINDER · `.mj-finder` ─────────────────────────────────
   Brought back from the launch build at Lucky's request, 2026-08-21:

     "I don't really like the 'which should you pick' element ... it takes a lot
      of space and it's not that fully interactive ... I prefer something
      conversational and interactive, something that proves there is application
      of thought in helping the reader."

   WHAT IT REPLACES, AND WHY THAT IS THE POINT. `.mj-rec` renders six static
   condition/pick boxes and six red buttons, 624px of them, in the final verdict
   — the moment the reader is closest to deciding and least able to afford a
   wall. It also makes the reader do the matching: here are six situations, work
   out which one is you. This element does that work for them and takes about
   half the height at rest.

   THE OLD MARKUP CARRIED GENERIC CLASS NAMES — `.fh`, `.fq`, `.opt`, `.fr` —
   scoped only by descent from `.mj-finder`. Four names any future element could
   want, kept apart by nothing but a parent selector. Rebuilt on the library's
   `block__part` naming so the CSS survives being moved, reused, or nested.

   EVERY OUTCOME IS SERVER-RENDERED. inserts.js only swaps which one is showing;
   it never writes a price, a name or a URL. That is what keeps this compliant
   with §11.2 (no raw affiliate URL — the old version hard-coded two of them
   inside the script) and §11.8 (the judgement is never gated: with JavaScript
   off, the reader gets the desk's standing pick and every question still reads).

   §11.6 — a pick with no stated trade-off is prohibited — is why `__rt` is not
   optional. Every outcome, including the tie, has to say what it costs you. */
/* WHITE, NOT NEARLY-WHITE. Lucky: "the background should always be white for
   these sections so that they can stand out on the pages."
   The card was --mj-surface (#FDFDFD) sitting on --mj-surface-alt (#FAF9F6):
   three points of luminance apart, which is a difference you can measure and
   cannot see. Pure white plus a soft lift is what separates an interactive
   block from the prose around it without drawing a heavier border. */
.mj-finder{ max-width:var(--article-width); margin:var(--sp-6) auto;
  border:1px solid var(--mj-rule); border-top:3px solid var(--mj-red);
  border-radius:var(--radius-md); background:#fff; overflow:hidden;
  box-shadow:0 1px 2px rgba(10,10,10,.04), 0 8px 24px -12px rgba(10,10,10,.10) }
body.dark-mode .mj-finder{ background:#161618; border-color:#2a2a2d;
  border-top-color:var(--mj-red); box-shadow:none }

/* THE OPENING HAS TO EARN THE 30 SECONDS IT ASKS FOR. Lucky: "maybe what you
   could have changed was to just make the information at the top stand out."
   A faint tint and a rule under it turn the head from three stacked lines into
   a zone — it reads as the element introducing itself, which is what makes a
   reader willing to touch the first pill. */
/* AND IT BELONGS TO THE STANDALONE ELEMENT ONLY (Lucky, 2026-09-16).
   "Be careful not to add it with that shadow that starts off with faint red and
   then becomes white — that specific variant is only suitable on the 'Which one
   is actually right for you?' element." Nested under a table or inside a panel,
   a red-to-white wash reads as a second element bleeding into its host. The
   full finder introduces itself; an embedded one is already introduced by the
   thing it sits in. */
.mj-finder:not([data-layout="nudge"]):not([data-layout="embed"]) .mj-finder__head{
  padding:var(--sp-5); background:linear-gradient(180deg,
  rgba(236,34,39,.045), rgba(236,34,39,0)) }
body.dark-mode .mj-finder:not([data-layout="nudge"]):not([data-layout="embed"]) .mj-finder__head{
  background:linear-gradient(180deg, rgba(236,34,39,.09), rgba(236,34,39,0)) }
.mj-finder__head{ padding:var(--sp-5) }
/* THE PROMISE HAS TO BE READABLE AS A PROMISE. Lucky: "spice it up a little so
   people can understand that it takes 30 seconds." As flat micro-caps it read
   as a label; as a badge in the display face, on its own tint, it reads as an
   offer of how little this will cost the reader. Small — but small in the
   TECHNO face, which is the difference between quiet and weak. */
.mj-finder__k{ display:inline-flex; align-items:center; gap:7px;
  font-family:var(--font-display); font-weight:700; font-size:.62rem;
  letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-red);
  background:rgba(236,34,39,.08); border:1px solid rgba(236,34,39,.22);
  border-radius:999px; padding:5px 11px 5px 9px; margin:0 0 9px }
.mj-finder__k::before{ content:""; width:7px; height:7px; flex:0 0 auto;
  background:var(--mj-red) }
body.dark-mode .mj-finder__k{ background:rgba(236,34,39,.14);
  border-color:rgba(236,34,39,.34) }
.mj-finder__h{ font-family:var(--font-display); font-weight:800; font-size:1.24rem;
  line-height:1.25; color:var(--mj-black); margin:0 0 5px }
body.dark-mode .mj-finder__h{ color:#fff }
.mj-finder__sub{ font-size:.92rem; line-height:1.55; color:var(--mj-text-muted); margin:0 }
body.dark-mode .mj-finder__sub{ color:#b6b6bb }

/* The questions band carries no padding of its own — each question owns its
   own, so a folded one takes up exactly nothing rather than leaving a gap where
   it used to be. */
.mj-finder__qs{ display:block }
.mj-finder__q{ padding:var(--sp-4) var(--sp-5); border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-finder__q{ border-color:#242427 }
/* PROGRESSIVE, NOT HIDDEN. The server sends every question open. inserts.js
   adds this attribute to pace them one at a time, which is the whole reason the
   block is "less in your face" at rest. Without the script nothing is folded —
   content never disappears because of JavaScript, only because of an answer. */
.mj-finder__q[data-folded]{ display:none }
.mj-finder__qt{ display:flex; align-items:baseline; gap:10px;
  font-family:var(--font-display); font-weight:700; font-size:.98rem;
  line-height:1.35; color:var(--mj-black); margin:0 0 11px }
body.dark-mode .mj-finder__qt{ color:#f2f2f2 }
.mj-finder__qn{ font-family:var(--font-meta); font-size:.68rem; font-weight:600;
  color:var(--mj-red); flex:0 0 auto; font-variant-numeric:tabular-nums }
.mj-finder__ql{ min-width:0 }
.mj-finder__opts{ display:flex; flex-wrap:wrap; gap:8px }

/* THE PILL IS THE INTERACTION, so it gets a real one: hover, focus and pressed
   are three different things and all three are visible. `aria-pressed` carries
   the state, so the styling and the screen reader cannot disagree. */
.mj-finder__opt{ font-family:var(--font-body); font-size:.88rem; line-height:1.3;
  padding:9px 15px; border:1px solid var(--mj-gray-300); border-radius:999px;
  background:transparent; color:var(--mj-charcoal); cursor:pointer;
  transition:background .15s ease, border-color .15s ease, color .15s ease }
body.dark-mode .mj-finder__opt{ color:#cfcfd2; border-color:#3a3a3d }
.mj-finder__opt:hover{ border-color:var(--mj-red); color:var(--mj-red) }
.mj-finder__opt:focus-visible{ outline:2px solid var(--mj-red); outline-offset:2px }
.mj-finder__opt[aria-pressed="true"]{ background:var(--mj-ink-900); color:#fff;
  border-color:var(--mj-ink-900) }
body.dark-mode .mj-finder__opt[aria-pressed="true"]{ background:#fff; color:#111;
  border-color:#fff }

/* THE RESULT IS A DIFFERENT GROUND, not a different border. The panel already
   has a marker at the top; a second bar down the side of the result would be
   the same three-nested-boxes noise the inline reveal had. Ground does the job. */
/* THE ANSWER IS BLACK, NOT GREY. Lucky: "make sure that the bottom section is
   not gray because gray is too dull … give it a red or a black and make the
   text stand out."

   BLACK, and not red, for a reason that decides itself: the recommendation
   button inside this band is red, and red is this site's action colour
   everywhere. A red band would swallow the one element that has to be the
   loudest thing in the element. Black is already where this desk puts its
   conclusions — the summary strip, the comparison header, the closing CTA — so
   the finder's answer landing on black says "this is the verdict" in a vocabulary
   the reader has already met three times before they reach it. */
/* BLACK UNTIL THEY ANSWER, RED ONCE THEY HAVE. Lucky: "the CTA section of these
   elements should also be red or black depending on the choices that the
   customer is making … I don't know which stands out more."

   The answer is both, and the difference is the point. BLACK is the desk's
   standing position — the pick we publish for everyone, calm and authoritative.
   RED is this site's action colour, so the moment the panel is answering THIS
   reader it changes ground and the block stops being an article and starts
   being a decision. One element, two temperatures, and the reader is told which
   one they are looking at without a word being spent on it.

   On red the lead button cannot also be red, so it inverts to solid white —
   which is the strongest button on the whole site and is exactly where it
   should be: under an answer the reader asked for. */
.mj-finder__result{ padding:var(--sp-5); background:var(--mj-ink-900);
  border-top:3px solid var(--mj-red); transition:background .25s ease }
body.dark-mode .mj-finder__result{ background:#0d0d0f; border-top-color:var(--mj-red) }
.mj-finder__result[data-state="answered"],
body.dark-mode .mj-finder__result[data-state="answered"]{ background:var(--mj-red);
  border-top-color:var(--mj-ink-900) }
.mj-finder__result[data-state="answered"] .mj-finder__rk{ color:rgba(255,255,255,.82) }
.mj-finder__result[data-state="answered"] .mj-finder__rw{ color:rgba(255,255,255,.90) }
.mj-finder__result[data-state="answered"] .mj-finder__rt{ color:rgba(255,255,255,.82) }
/* THE LABEL INSIDE THE TRADE-OFF LINE TOO. `.mj-finder__rt` was inverted and
   `.mj-finder__rtk` — the bold "The trade-off:" inside it — sets its own near-
   black, which wins over the parent's inherited white. So on the red band the
   one phrase that introduces the caveat rendered black on red: legible enough
   to notice and wrong enough to look broken. Every colour a state inverts has
   to be enumerated; inheriting is not the same as being set. */
.mj-finder__result[data-state="answered"] .mj-finder__rtk{ color:#fff }
.mj-finder__result[data-state="answered"] .mj-finder__rtv{ color:rgba(255,255,255,.82) }
.mj-finder__result[data-state="answered"] .mj-finder__note{ color:rgba(255,255,255,.62) }
/* the lead button inverts to white on red; the alternative stays outlined */
.mj-finder__result[data-state="answered"] .mj-finder__cta[data-role="lead"]{
  background:#fff; color:var(--mj-ink-900); border-color:#fff }
.mj-finder__result[data-state="answered"] .mj-finder__cta[data-role="lead"]:hover{
  background:var(--mj-ink-900); color:#fff; border-color:var(--mj-ink-900) }
.mj-finder__rk{ font-family:var(--font-display); font-weight:700; font-size:.62rem;
  letter-spacing:1.5px; text-transform:uppercase; color:var(--mj-red); margin:0 0 7px }
.mj-finder__rh{ font-family:var(--font-display); font-weight:800; font-size:1.28rem;
  line-height:1.22; color:#fff; margin:0 0 9px }
body.dark-mode .mj-finder__rh{ color:#fff }
.mj-finder__rw{ font-size:.94rem; line-height:1.6; color:#c9c9ce; margin:0 0 8px }
body.dark-mode .mj-finder__rw{ color:#c9c9ce }
.mj-finder__rt{ font-size:.88rem; line-height:1.55; color:#a9a9b0; margin:0 }
/* THE LABEL IS FURNITURE, THE CONSEQUENCE IS DATA. "The trade-off:" is fixed in
   the template so every outcome is forced to answer the same question in the
   same words, and so the script can set one text node instead of rebuilding
   markup. */
.mj-finder__rtk{ color:#fff }
/* The consequence half. Its own element so the script sets one text node and
   the bold label survives every outcome swap. */
.mj-finder__rtv{ color:inherit }
body.dark-mode .mj-finder__rtk{ color:#fff }

/* ONE ACTION LOOKS LIKE THE ANSWER AND THE OTHER LOOKS LIKE AN ALTERNATIVE.
   That difference is the recommendation — if both buttons are red the element
   has picked nothing and is just two adverts with a quiz on top. */
.mj-finder__actions{ display:flex; flex-wrap:wrap; gap:10px; margin-top:var(--sp-4) }
/* THE RECOMMENDATION LEADS THE ROW (J-7a, 2026-10-04): on a Mailchimp answer the first button
   read "Start free at MailerLite". Server order is fixed; the role the script sets decides. */
.mj-finder__cta[data-role="lead"]{ order:-1 }
.mj-finder__cta[data-role="both"]{ order:1 }
.mj-finder__cta{ display:inline-flex; align-items:center;
  font-family:var(--font-display); font-size:.76rem; font-weight:600;
  letter-spacing:.6px; text-transform:uppercase; text-decoration:none;
  color:#fff; background:var(--mj-red); padding:11px var(--sp-5);
  border:1px solid var(--mj-red); border-radius:var(--radius-sm);
  transition:background .15s ease, color .15s ease }
.mj-finder__cta:hover{ background:var(--mj-red-hover,#c31b20); border-color:var(--mj-red-hover,#c31b20) }
/* LEAD vs ALT IS A DATA ATTRIBUTE, NOT A MODIFIER CLASS. Composition law 2: a
   structural difference gets its own template, a presentational one travels as
   data. These two anchors are the same anchor — only which of them is the
   recommendation changes, and it changes at runtime as the reader answers. An
   attribute lets the server set the opening state and the script move it
   without either of them touching class lists. */
.mj-finder__cta[data-role="alt"]{ background:transparent; color:#f0f0f2;
  border-color:rgba(255,255,255,.32) }
.mj-finder__cta[data-role="alt"]:hover{ background:rgba(255,255,255,.06); color:#fff;
  border-color:#fff }
body.dark-mode .mj-finder__cta[data-role="alt"]{ color:#f0f0f2;
  border-color:rgba(255,255,255,.28) }
/* `hidden` cannot carry this: it is a BOOLEAN attribute, so a slot rendering
   hidden="" would hide every button rather than none. The state travels as data
   like the rest of this element's runtime state. */
.mj-finder__cta[data-shown="no"]{ display:none }
/* "ASK BOTH TO QUOTE ME" — the tie's third way (c-gvx7, 2026-09-17). An outline like the
   alternative, marked with a plus so it reads as "both of them", not as a third pick. */
.mj-finder__cta[data-role="both"]{ background:transparent; color:#f0f0f2;
  border:1px dashed rgba(255,255,255,.55) }
.mj-finder__cta[data-role="both"]::before{ content:"+ "; font-weight:800 }
.mj-finder__cta[data-role="both"]:hover{ background:rgba(255,255,255,.08); color:#fff; border-style:solid; border-color:#fff }

/* THE OUTCOMES ARE DATA, NOT DECORATION. They live inside a <template>, so they
   never render and never paint — but they carry a class so a gate can address
   them, which is the whole lesson of the review block that published a rival's
   rate from cells nothing could select. */
.mj-finder__outcome{ display:none }
.mj-finder__note{ font-family:var(--font-meta); font-size:.66rem; letter-spacing:.2px;
  line-height:1.45; color:#8a8a92; margin:var(--sp-4) 0 0 }

@media(max-width:520px){
  .mj-finder__actions{ flex-direction:column; align-items:stretch }
  .mj-finder__cta{ justify-content:center }
}

/* =============================================================================
   TIER-1 INSERTS — built 2026-07-30 (Opus 5, design authority)
   Design directives v3: square-first (G1), brand colour with intent (G2/G3),
   interaction states required (G5), mashjoy markers not dots (G6).
   ============================================================================= */

/* --- smart-editors-note (CPT: smart-editors-note) --------------------------
   THE COLLAPSE MECHANISM. This is how a 2,000-word page keeps 4,000 words of
   depth: detail a minority of readers need is present, crawlable and closed.
   Uses native <details>/<summary> — keyboard accessible, no JS, and the content
   stays in the DOM for E-E-A-T. Reading-experience contract GATE R5 routes
   parenthetical asides, small print and "the long version" in here.
   Variants: --caveat (honest warning) · --method (how we tested) · --spec (the
   long spec sheet). Default is editorial.
   -------------------------------------------------------------------------- */
.mj-note{ margin:var(--sp-6) 0; background:var(--mj-surface);
  border:1px solid var(--mj-rule); border-left:4px solid var(--mj-ink-900);
  border-radius:0 var(--radius-md) var(--radius-md) 0; overflow:hidden }
.mj-note--caveat{ border-left-color:var(--mj-red) }
.mj-note--method{ border-left-color:var(--mj-trust) }
.mj-note--spec{ border-left-color:var(--mj-growth) }
body.dark-mode .mj-note{ background:#131315; border-color:#2a2a2d }

.mj-note>summary{ display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5); cursor:pointer; list-style:none;
  font-family:var(--font-display); font-weight:700; font-size:.95rem;
  color:var(--mj-black); transition:background .16s ease, color .16s ease }
.mj-note>summary::-webkit-details-marker{ display:none }
.mj-note>summary:hover{ background:var(--mj-surface-2); color:var(--mj-trust) }
.mj-note>summary:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:-2px }
body.dark-mode .mj-note>summary{ color:#fff }
body.dark-mode .mj-note>summary:hover{ background:#1b1b1e }

/* the marker: a mashjoy square that rotates to a "minus" feel when open (G1/G6) */
.mj-note__mk{ flex:0 0 auto; width:11px; height:11px; border-radius:1px;
  background:var(--mj-ink-900); transition:transform .2s ease, background .2s ease }
.mj-note--caveat .mj-note__mk{ background:var(--mj-red) }
.mj-note--method .mj-note__mk{ background:var(--mj-trust) }
.mj-note--spec   .mj-note__mk{ background:var(--mj-growth) }
.mj-note[open] .mj-note__mk{ transform:rotate(45deg) scale(.82) }
body.dark-mode .mj-note__mk{ background:#fff }

/* THE HEADLINE IS A SLOT, NOT PROTOTYPE COPY (2026-08-27). It had no class, so
   extract.py had nothing to target, and every editor's note on the site shipped
   the demo string "Why the R0 data line actually matters" while the note's real
   subject was demoted to the micro-caps tail. That is the balance fault Lucky
   saw: a big wrong heading beside a small right one. */
.mj-note__hd{ flex:1 1 auto; min-width:0 }

.mj-note__lbl{ font-family:var(--font-meta); font-size:.66rem; letter-spacing:1.5px;
  text-transform:uppercase; color:var(--mj-text-muted); margin-left:auto;
  white-space:nowrap; font-weight:700 }

/* THE AFFORDANCE HAS TO BE WORDS (Lucky, 2026-08-27): "advise readers that they
   can click to expand ... wherever it occurs or when it is used." A caret is a
   convention a reader has to already know; a pill that says so is not. It lives
   in CSS rather than in the markup so it arrives on every collapsed element of
   this family at once, and cannot be forgotten by an author. */
.mj-note>summary::after{ content:"Click to expand"; flex:0 0 auto;
  font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:var(--mj-trust);
  border:1px solid currentColor; border-radius:999px; padding:3px 9px;
  white-space:nowrap; opacity:.9; transition:opacity .15s ease }
.mj-note[open]>summary::after{ content:"Click to close"; color:var(--mj-text-muted);
  opacity:.7 }
.mj-note>summary:hover::after{ opacity:1 }
.mj-note__body{ padding:0 var(--sp-5) var(--sp-5) calc(var(--sp-5) + 11px + var(--sp-3));
  font-size:.95rem; line-height:1.7; color:var(--mj-text-muted) }
.mj-note__body>*:first-child{ margin-top:0 }
.mj-note__body>*:last-child{ margin-bottom:0 }
.mj-note__body p{ margin:0 0 var(--sp-3) }

@media(max-width:520px){
  .mj-note>summary{ padding:var(--sp-3) var(--sp-4); font-size:.9rem; flex-wrap:wrap }
  .mj-note__hd{ flex-basis:calc(100% - 11px - var(--sp-3)) }
  .mj-note__lbl{ margin-left:0; padding-left:calc(11px + var(--sp-3)) }
  .mj-note>summary::after{ content:"Tap to expand"; margin-left:auto; font-size:.56rem }
  .mj-note[open]>summary::after{ content:"Tap to close" }
  .mj-note__body{ padding:0 var(--sp-4) var(--sp-4) var(--sp-4) }
}

/* --- glossary-term (CPT: glossary-term) ------------------------------------
   Defined ONCE site-wide, referenced everywhere (GATE R5). Desktop: hover
   reveals the definition. Mobile/touch: tap toggles it (no hover to rely on,
   per G5). The term itself stays readable inline — a dotted underline in trust
   blue, never a link colour, because it does not navigate.
   -------------------------------------------------------------------------- */
/* the trigger is a <button> for keyboard + screen-reader correctness, so every
   UA button default has to be reset back to prose before we style it */
/* THE PANEL MUST NOT LIVE INSIDE THE BUTTON.
   It shipped as `<button>…<a href="/glossary/#term">…</a></button>` — interactive
   content nested inside interactive content, which is invalid, unreachable by
   keyboard in the inner link, and the reason the panel could not hold anything
   the reader might operate. `__wrap` takes over the positioning so the panel is
   a SIBLING of the trigger and may legally contain its own controls. Everything
   here stays phrasing content: the term sits in running prose inside a <p>, so
   no <div>, no <p>, and no <details> — the disclosure is a button and a span. */
.mj-gloss__wrap{ position:relative; display:inline }
.mj-gloss{ display:inline; margin:0; padding:0;
  background:none; border:0; border-bottom:2px dotted var(--mj-trust);
  font:inherit; text-align:inherit; letter-spacing:inherit; -webkit-appearance:none; appearance:none;
  color:inherit; cursor:help; font-weight:600 }
.mj-gloss:hover,.mj-gloss:focus-visible{ border-bottom-style:solid; color:var(--mj-trust) }
.mj-gloss:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px; border-radius:1px }

.mj-gloss__pop{ position:absolute; left:0; top:calc(100% + 10px); z-index:60;
  width:min(320px,78vw); padding:var(--sp-4); text-align:left;
  background:var(--mj-ink-900); color:#fff; border-radius:var(--radius-sm);
  font-family:var(--font-body); font-weight:400; font-size:.86rem; line-height:1.6;
  box-shadow:0 10px 30px rgba(0,0,0,.22);
  opacity:0; visibility:hidden; transform:translateY(-4px);
  transition:opacity .16s ease, transform .16s ease, visibility .16s }
.mj-gloss__pop::before{ content:""; position:absolute; top:-5px; left:14px;
  width:10px; height:10px; background:var(--mj-ink-900); transform:rotate(45deg) }
/* edge-aware flip: a term near the right margin would clip the panel off-screen.
   .is-flipped is set by the glossary script on hover/open; the CSS is the contract. */
.mj-gloss__wrap.is-open .mj-gloss__pop{ transform:translateY(0) }
/* VERTICAL PLACEMENT. Expanded, the panel is ~610px — taller than the space
   below a term in the lower half of a laptop viewport. `.is-above` anchors it to
   the term's top instead, and the max-height is the last resort so a panel can
   never run off the screen with its glossary link unreachable. */
.mj-gloss__pop{ max-height:calc(100vh - 32px); overflow-y:auto; overscroll-behavior:contain }
.mj-gloss__pop.is-above{ top:auto; bottom:calc(100% + 10px) }
.mj-gloss__pop.is-above::before{ top:auto; bottom:-5px }
.mj-gloss__pop.is-above::after{ top:auto; bottom:-12px }
.mj-gloss__pop.is-flipped{ left:auto; right:0 }
.mj-gloss__pop.is-flipped::before{ left:auto; right:14px }
.mj-gloss__wrap:hover .mj-gloss__pop,
.mj-gloss:focus-visible + .mj-gloss__pop,
.mj-gloss__wrap:focus-within .mj-gloss__pop,
.mj-gloss__wrap.is-open .mj-gloss__pop{ opacity:1; visibility:visible; transform:translateY(0) }
/* A CLOSED PANEL MUST NOT WIDEN THE PAGE (Phase 9 render audit, 2026-09-13).
   Hidden with `visibility`, a 320px panel anchored to a term near the right
   margin still counted toward the page's scroll width: card-machine-fees scrolled
   sideways by 133px at 800px, and cheapest, the iKhokha review and the fees page
   did too, with every build gate green. A fixed box adds no scrollable overflow,
   so the panel is fixed while closed and returns to its anchor when it opens.
   gloss.js measures `offsetWidth`, which a fixed, hidden box still reports. */
.mj-gloss__wrap:not(:hover):not(:focus-within):not(.is-open) .mj-gloss__pop{
  position:fixed; left:0; right:auto; top:0; bottom:auto;
  /* HIDDEN IN THE SAME FRAME IT MOVES (J-7a, Lucky 2026-10-04): this rule jumps the panel
     to the top-left the instant the pointer leaves, and the .16s fade-out then showed it
     there. Position cannot be transitioned, so closing is instant; opening still fades. */
  transition:none; opacity:0; visibility:hidden }
/* the mouse has to CROSS the gap between the term and the panel. Without a
   bridge the panel closes halfway there, which is why a hover panel holding
   controls needs one. */
.mj-gloss__pop::after{ content:""; position:absolute; top:-12px; left:0; right:0; height:12px }
.mj-gloss__t{ display:block; font-family:var(--font-display); font-weight:700;
  font-size:.9rem; color:#fff; margin-bottom:4px }
.mj-gloss__d{ display:block }

/* THE WORKED EXAMPLE — the definition in rands.
   Lucky: "try to bring things into reality or the perspective of the realism of
   the article." A correct definition the reader cannot price is still homework.
   This band is where the term stops being vocabulary and becomes money. */
.mj-gloss__eg{ display:block; margin-top:var(--sp-3); padding:10px var(--sp-3);
  background:rgba(255,255,255,.07); border-left:2px solid var(--mj-growth);
  border-radius:0 var(--radius-sm) var(--radius-sm) 0 }
.mj-gloss__eg-k{ display:block; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:1px; text-transform:uppercase; font-weight:700;
  color:var(--mj-growth-text); margin-bottom:3px }
.mj-gloss__eg-b{ display:block; font-size:.82rem; line-height:1.55; color:#f2f2f4 }

/* THE FULL DEFINITION OPENS HERE, not on another page.
   It was a link to /glossary/#term — which, measured, held FEWER words than the
   tooltip the reader was already looking at. So the link cost them the article
   and paid them nothing. */
.mj-gloss__full{ display:block; margin-top:var(--sp-3); padding-top:var(--sp-3);
  border-top:1px solid rgba(255,255,255,.16); font-size:.82rem; line-height:1.6;
  color:#e4e4e8 }
.mj-gloss__full[hidden]{ display:none }
.mj-gloss__link{ display:block; margin-top:10px; font-family:var(--font-meta);
  font-size:.62rem; letter-spacing:1px; text-transform:uppercase; font-weight:700;
  color:#7fb2ff; text-decoration:none }
.mj-gloss__link:hover{ color:#fff; text-decoration:underline }
.mj-gloss__more{ display:inline-block; margin-top:var(--sp-3); padding:0;
  background:none; border:0; cursor:pointer;
  font-family:var(--font-meta); font-size:.66rem; letter-spacing:1px;
  text-transform:uppercase; font-weight:700; color:#7fb2ff; text-decoration:none }
.mj-gloss__more::after{ content:" \2193"; }
.mj-gloss__more[aria-expanded="true"]::after{ content:" \2191" }
.mj-gloss__more:hover{ color:#fff; text-decoration:underline }
.mj-gloss__more:focus-visible{ outline:2px solid #7fb2ff; outline-offset:2px }

/* touch devices: hover is unreliable, so the .is-open class (tap) drives it */
@media(hover:none){
  .mj-gloss{ border-bottom-style:solid }
  .mj-gloss__wrap:hover .mj-gloss__pop{ opacity:0; visibility:hidden }
  .mj-gloss__wrap.is-open .mj-gloss__pop{ opacity:1; visibility:visible }
}
@media(max-width:520px){
  .mj-gloss__pop{ position:fixed; left:var(--sp-4); right:var(--sp-4); width:auto;
    top:auto; bottom:var(--sp-4); max-height:72vh; box-shadow:0 -6px 30px rgba(0,0,0,.3) }
  /* the sheet is already bottom-anchored on a phone; the flips do not apply */
  .mj-gloss__pop.is-above,.mj-gloss__pop.is-flipped{ top:auto; bottom:var(--sp-4);
    left:var(--sp-4); right:var(--sp-4) }
  .mj-gloss__pop::before{ display:none }
}

/* --- pros-cons (CPT: pros-cons) --------------------------------------------
   The most-screenshotted unit on any review, so it carries the attribution mark
   (v3 G4). Two columns of equal weight — we never visually favour one side; the
   verdict does that job. Square markers only (G1/G6), growth green vs alert red
   as the fixed semantic (G2). Stacks at 620px with the pros first.
   -------------------------------------------------------------------------- */
.mj-pc{ margin:var(--sp-6) 0; border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  background:var(--mj-surface); overflow:hidden }
body.dark-mode .mj-pc{ background:#131315; border-color:#2a2a2d }
.mj-pc__head{ display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-3) var(--sp-4);
  background:var(--mj-ink-900); color:#fff; font-family:var(--font-meta); font-size:.68rem;
  letter-spacing:1.6px; text-transform:uppercase; font-weight:700 }
/* ── THE VENDOR MARK IN A COMPARISON HEAD · observation 9.1 ──────────────
       "All pros-cons inserts should include vendor/company logos at the heading
        of each item being compared … visually identifying which product or
        company the pros and cons belong to."

   The head is an ink bar, so the mark sits on dark and needs no box of its own.
   It is capped small deliberately: it identifies the party, it does not
   advertise them, and a comparison whose heads look like banner ads has stopped
   being a comparison.

   A COMPARISON MUST PRESENT ITS PARTIES EQUALLY. Two of the four vendor logos in
   this repo are 80-byte 404 bodies saved with an .svg extension, so a naive
   implementation would have put a mark beside iKhokha and nothing beside Yoco —
   which reads as editorial preference rather than a missing file. The renderer
   therefore draws logos only when EVERY party in the element has one. */
.mj-pc__logo{ flex:0 0 auto; width:20px; height:20px; object-fit:contain;
  background:#fff; padding:2px; border-radius:var(--radius-sm) }
.mj-pc__name{ min-width:0 }

.mj-pc__head .mj-attrib{ color:#fff; opacity:.85 }
.mj-pc__grid{ display:grid; grid-template-columns:1fr 1fr }
.mj-pc__col{ padding:var(--sp-5) }
.mj-pc__col + .mj-pc__col{ border-left:1px solid var(--mj-rule) }
body.dark-mode .mj-pc__col + .mj-pc__col{ border-left-color:#2a2a2d }
.mj-pc__t{ display:flex; align-items:center; gap:8px; margin:0 0 var(--sp-4);
  font-family:var(--font-display); font-weight:800; font-size:.95rem; letter-spacing:.3px;
  text-transform:uppercase }
.mj-pc__t--pro{ color:#08694c } .mj-pc__t--con{ color:var(--mj-alert) }
body.dark-mode .mj-pc__t--pro{ color:#34d8a6 } body.dark-mode .mj-pc__t--con{ color:#ff8b93 }
.mj-pc ul{ list-style:none; margin:0; padding:0 }
/* A SENTENCE, NOT A ROW (J-7a, 2026-10-04): this was display:flex, so the moment a rand
   figure was wrapped in a span, "About R373 to R724 a month less…" broke into columns.
   The marker sits in a gutter instead (render_audit check 8). */
.mj-pc li{ position:relative; display:block; padding:7px 0 7px 24px; font-size:.95rem; line-height:1.6;
  color:var(--mj-ink) }
body.dark-mode .mj-pc li{ color:#e8e8ea }
/* Ticks and minus, not square bullets. A pros/cons list is a JUDGEMENT, and the
   grammatically right marker for "this is good / this is not" is a tick and a minus —
   a decorative square says nothing. Column HEADINGS carry no marker at all: a heading
   is not a list item, and bulleting it was a habit rather than a decision. */
.mj-pc li::before{ position:absolute; left:0; top:7px; width:14px; font-weight:800;
  font-size:.95em; line-height:1.6; text-align:center }
.mj-pc__col--pro li::before{ content:"✓"; color:#08694c }
.mj-pc__col--con li::before{ content:"–"; color:var(--mj-alert) }
body.dark-mode .mj-pc__col--pro li::before{ color:#34d8a6 }
body.dark-mode .mj-pc__col--con li::before{ color:#ff8b93 }
.mj-pc__foot{ padding:var(--sp-3) var(--sp-4); border-top:1px solid var(--mj-rule);
  font-family:var(--font-meta); font-size:.7rem; color:var(--mj-gray-400) }
@media(max-width:620px){
  .mj-pc__grid{ grid-template-columns:1fr }
  .mj-pc__col + .mj-pc__col{ border-left:0; border-top:1px solid var(--mj-rule) }
}

/* --- scenario (CPT: scenario) -----------------------------------------------
   "If you turn over R10k a month…" — the branch block. The biggest single prose
   consumer in the reviews, because writers otherwise spell every branch out in
   paragraphs. Each branch is a row: the CONDITION reads as a label, the ANSWER
   carries the weight. Hover lifts the row (G5).
   -------------------------------------------------------------------------- */
.mj-scn{ margin:var(--sp-6) 0; border:1px solid var(--mj-rule); border-left:4px solid var(--mj-trust);
  border-radius:0 var(--radius-md) var(--radius-md) 0; background:var(--mj-surface); overflow:hidden }
body.dark-mode .mj-scn{ background:#131315; border-color:#2a2a2d; border-left-color:var(--mj-trust) }
.mj-scn__h{ display:flex; align-items:center; gap:8px; padding:var(--sp-4) var(--sp-5) var(--sp-2);
  font-family:var(--font-display); font-weight:800; font-size:1rem; color:var(--mj-black) }
body.dark-mode .mj-scn__h{ color:#fff }
.mj-scn__row{ display:grid; grid-template-columns:minmax(150px,.42fr) 1fr; gap:var(--sp-4);
  padding:var(--sp-4) var(--sp-5); border-top:1px solid var(--mj-rule);
  transition:background .16s ease }
body.dark-mode .mj-scn__row{ border-top-color:#2a2a2d }
.mj-scn__row:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-scn__row:hover{ background:#1b1b1e }
.mj-scn__if{ font-family:var(--font-display); font-weight:700; font-size:.9rem; line-height:1.45;
  color:var(--mj-trust) }
.mj-scn__if::before{ content:"IF"; display:block; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:1.6px; color:var(--mj-gray-400); margin-bottom:4px }
.mj-scn__then{ font-size:.95rem; line-height:1.65; color:var(--mj-ink) }
body.dark-mode .mj-scn__then{ color:#e8e8ea }
.mj-scn__then b,.mj-scn__then strong{ color:var(--mj-black) }
body.dark-mode .mj-scn__then b,body.dark-mode .mj-scn__then strong{ color:#fff }
@media(max-width:620px){
  .mj-scn__row{ grid-template-columns:1fr; gap:6px; padding:var(--sp-4) }
  .mj-scn__h{ padding:var(--sp-4) var(--sp-4) var(--sp-2) }
}

/* --- table-of-contents (CPT: table-of-contents) -----------------------------
   Two forms from ONE markup contract: the inline "In this guide" block, and the
   sticky companion rail (.mj-toc--rail) that docks with the C1 slim header.
   GATE R8: every article over 1,200 words carries one straight after the intro.
   Replaces the hand-typed jump list, which is what produced the 137 relative 404s.
   -------------------------------------------------------------------------- */
.mj-toc{ margin:var(--sp-6) 0; border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  background:var(--mj-surface-2); padding:var(--sp-5) }
body.dark-mode .mj-toc{ background:#141416; border-color:#2a2a2d }
.mj-toc__h{ display:flex; align-items:center; gap:8px; margin:0 0 var(--sp-3);
  font-family:var(--font-meta); font-size:.68rem; letter-spacing:1.6px; text-transform:uppercase;
  font-weight:700; color:var(--mj-text-muted) }
.mj-toc ol{ list-style:none; margin:0; padding:0; counter-reset:mjtoc }
.mj-toc li{ counter-increment:mjtoc; border-top:1px solid var(--mj-rule) }
.mj-toc li:first-child{ border-top:0 }
body.dark-mode .mj-toc li{ border-top-color:#2a2a2d }
.mj-toc a{ display:flex; align-items:baseline; gap:var(--sp-3); padding:9px 0;
  text-decoration:none; color:var(--mj-ink); font-size:.95rem; line-height:1.5;
  transition:color .16s ease }
body.dark-mode .mj-toc a{ color:#e8e8ea }
.mj-toc a::before{ content:counter(mjtoc,decimal-leading-zero); flex:0 0 auto;
  font-family:var(--font-display); font-weight:800; font-size:.75rem; color:var(--mj-gray-400);
  transition:color .16s ease }
.mj-toc a:hover{ color:var(--mj-trust) }            /* blue = navigation, per the menus */
.mj-toc a:hover::before{ color:var(--mj-trust) }
.mj-toc a:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
/* the sticky companion rail form */
.mj-toc--rail{ background:transparent; border:0; padding:0; margin:0;
  position:sticky; top:calc(var(--mj-header-top) + var(--sp-5)) }
.mj-toc--rail .mj-toc__h{ padding-bottom:var(--sp-2); border-bottom:2px solid var(--mj-black) }
body.dark-mode .mj-toc--rail .mj-toc__h{ border-bottom-color:#fff }
.mj-toc--rail a{ font-size:.88rem }
.mj-toc--rail li.is-current a{ color:var(--mj-trust); font-weight:700 }
@media(max-width:900px){ .mj-toc--rail{ display:none } }   /* rail is desktop-only; inline form covers the rest */

/* --- smart-tip (CPT: smart-tip) ---------------------------------------------
   Deliberately QUIETER than smart_callout, so the two never compete. A callout
   is a decision surface; a tip is a nudge. No panel, no fill — just a marker,
   a rule and a line of copy.
   -------------------------------------------------------------------------- */
.mj-tip{ display:grid; grid-template-columns:auto 1fr; gap:var(--sp-3);
  margin:var(--sp-5) 0; padding:var(--sp-3) 0; border-top:1px solid var(--mj-rule);
  border-bottom:1px solid var(--mj-rule); font-size:.95rem; line-height:1.65;
  color:var(--mj-text-muted) }
body.dark-mode .mj-tip{ border-color:#2a2a2d }
.mj-tip__mk{ flex:0 0 auto; width:9px; height:9px; margin-top:.5em; border-radius:1px;
  background:var(--mj-growth) }
.mj-tip__k{ font-family:var(--font-meta); font-size:.64rem; letter-spacing:1.6px;
  text-transform:uppercase; font-weight:700; color:#08694c; margin-right:6px }
/* the tip's prose needs a class of its own so extraction can slot it without
   swallowing the label span beside it (2026-08-27) */
.mj-tip__b{ display:inline }
body.dark-mode .mj-tip__k{ color:#34d8a6 }
.mj-tip p{ margin:0 }

/* --- smart_recommendation (CPT: smart-recommendation) -----------------------
   "For most people / for you." On a 2-product comparison this must NEVER read as
   a coronation (insert-matrix rule) — hence the segmented form, which gives each
   option its own equal panel. The single form is only for types where one pick is
   genuinely the answer.
   -------------------------------------------------------------------------- */
.mj-rec{ margin:var(--sp-6) 0; border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  overflow:hidden; background:var(--mj-surface) }
body.dark-mode .mj-rec{ background:#131315; border-color:#2a2a2d }
.mj-rec__h{ display:flex; align-items:center; gap:8px; padding:var(--sp-3) var(--sp-4);
  background:var(--mj-ink-900); color:#fff; font-family:var(--font-meta); font-size:.68rem;
  letter-spacing:1.6px; text-transform:uppercase; font-weight:700 }
.mj-rec__h .mj-attrib{ color:#fff; opacity:.85 }
.mj-rec__seg{ display:grid; grid-template-columns:1fr 1fr }
.mj-rec__opt{ padding:var(--sp-5); transition:background .16s ease }
.mj-rec__opt + .mj-rec__opt{ border-left:1px solid var(--mj-rule) }
body.dark-mode .mj-rec__opt + .mj-rec__opt{ border-left-color:#2a2a2d }
.mj-rec__opt:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-rec__opt:hover{ background:#1b1b1e }
.mj-rec__for{ display:flex; align-items:baseline; gap:8px; margin:0 0 6px;
  font-family:var(--font-meta); font-size:.64rem; letter-spacing:1.4px; text-transform:uppercase;
  color:var(--mj-gray-400); font-weight:700 }
.mj-rec__pick{ font-family:var(--font-display); font-weight:800; font-size:1.15rem;
  color:var(--mj-black); margin:0 0 6px; line-height:1.2 }
body.dark-mode .mj-rec__pick{ color:#fff }
.mj-rec__why{ font-size:.92rem; line-height:1.6; color:var(--mj-text-muted); margin:0 }
.mj-rec--single .mj-rec__seg{ grid-template-columns:1fr }
@media(max-width:620px){
  .mj-rec__seg{ grid-template-columns:1fr }
  .mj-rec__opt + .mj-rec__opt{ border-left:0; border-top:1px solid var(--mj-rule) }
}

/* --- The mashjoy NUMBERED marker (D6 / v3 G6) -------------------------------
   The counterpart to .mj-sq. Technor, squared, red or black. Replaces every
   circular rank badge and every default <ol> disc. Sizes track the square scale.
   -------------------------------------------------------------------------- */
.mj-num{ display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto;
  width:28px; height:28px; border-radius:2px; background:var(--mj-ink-900); color:#fff;
  font-family:var(--font-display); font-weight:800; font-size:.85rem; line-height:1;
  font-variant-numeric:tabular-nums }
.mj-num--red{ background:var(--mj-red) }
.mj-num--ghost{ background:transparent; color:var(--mj-black);
  box-shadow:inset 0 0 0 2px var(--mj-ink-900) }
.mj-num--sm{ width:22px; height:22px; font-size:.72rem }
.mj-num--lg{ width:36px; height:36px; font-size:1.05rem }
body.dark-mode .mj-num{ background:#fff; color:#0a0a0a }
body.dark-mode .mj-num--red{ background:var(--mj-red); color:#fff }
body.dark-mode .mj-num--ghost{ color:#fff; box-shadow:inset 0 0 0 2px #fff }
/* numbered list built on the same marker */
.mj-list--num{ list-style:none; counter-reset:mjnum; padding:0 }
.mj-list--num li{ counter-increment:mjnum; padding-left:2.4em; position:relative }
.mj-list--num li::before{ content:counter(mjnum); position:absolute; left:0; top:.05em;
  width:1.55em; height:1.55em; border-radius:2px; background:var(--mj-ink-900); color:#fff;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; font-size:.78em }
.mj-list--num.mj-list--red li::before{ background:var(--mj-red) }
body.dark-mode .mj-list--num li::before{ background:#fff; color:#0a0a0a }

/* --- Ranked-list rows are ALIVE (v3 G5) -------------------------------------
   Lucky's note on "THE 9, RANKED": the rows should respond. Blue on hover,
   because blue already means navigation everywhere else in the system.
   -------------------------------------------------------------------------- */
.mj-ranklist{ list-style:none; margin:0; padding:0 }
.mj-ranklist li{ border-top:1px solid var(--mj-rule) }
.mj-ranklist li:first-child{ border-top:0 }
body.dark-mode .mj-ranklist li{ border-top-color:#2a2a2d }
.mj-ranklist a{ display:flex; align-items:center; gap:var(--sp-3); padding:11px 6px;
  text-decoration:none; color:var(--mj-ink); transition:background .16s ease, color .16s ease }
body.dark-mode .mj-ranklist a{ color:#e8e8ea }
.mj-ranklist a:hover,.mj-ranklist a:focus-visible{ color:var(--mj-trust);
  background:var(--mj-surface-2) }
body.dark-mode .mj-ranklist a:hover{ background:#1b1b1e }
.mj-ranklist a:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:-2px }
.mj-ranklist__n{ flex:0 0 auto }
.mj-ranklist__t{ font-family:var(--font-display); font-weight:700; font-size:.98rem }
.mj-ranklist__for{ margin-left:auto; font-size:.85rem; color:var(--mj-gray-400);
  text-align:right; white-space:nowrap }
.mj-ranklist a:hover .mj-ranklist__for{ color:var(--mj-trust) }
@media(max-width:520px){
  .mj-ranklist__for{ display:none }        /* the label loses to the name on a phone */
}

/* --- Table rows respond too (v3 G5 + G3.4) ---------------------------------- */
.mj-tbl tbody tr{ transition:background .16s ease }
.mj-tbl tbody tr:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-tbl tbody tr:hover{ background:#1b1b1e }
/* G3.4: the row LABEL carries weight — it is the thing being compared */
.mj-tbl tbody td:first-child,.mj-tbl .row-label{ font-weight:700; color:var(--mj-black) }
body.dark-mode .mj-tbl tbody td:first-child,body.dark-mode .mj-tbl .row-label{ color:#fff }
/* G3.3: scroll/guide hints are a helpful tool, so they read as one */
.mj-tbl-hint,.mj-pb-compare-hint,.mj-cmp-hint{ font-weight:500; color:var(--mj-gray-400) }
/* Reverted 2026-07-30: red on a scroll hint pulled the eye away from the data it was
   pointing at. A guide hint should be quiet. Emphasis belongs on the FACTS in a caption
   (dates, prices, figures) — that is what .mj-cap-k below is for. */
.mj-cap-k{ font-weight:700; color:var(--mj-ink) }
body.dark-mode .mj-cap-k{ color:#e8e8ea }
.mj-cap-k--red{ color:#a8323a } .mj-cap-k--blue{ color:#2f5aa8 }   /* dull, never solid brand */

/* --- Sticky table header on long tables (v3 G11) ---------------------------- */
.mj-tbl-scroll,.mj-cmp-scroll,.mj-pb-compare-scroll{ max-height:none }

/* A CLASS NAMED `--scroll` HAD NEVER SCROLLED (2026-08-27).
   `.mj-tbl-scroll` carried only `max-height:none` and `overflow-x: visible`, so a
   table wider than the column pushed the whole DOCUMENT sideways on a phone — the
   one thing the responsive rule forbids outright. It went unnoticed because this
   class had only ever appeared in a prototype, where nothing measures the page.
   The name was a promise; this is the promise kept. */
.mj-tbl-scroll,.mj-cmp-scroll,.mj-pb-compare-scroll{
  overflow-x:auto;
  -webkit-overflow-scrolling:touch;
  overscroll-behavior-x:contain }
.mj-tbl--sticky thead th{ position:sticky; top:0; z-index:5 }
.mj-tbl--sticky thead th::after{ content:""; position:absolute; left:0; right:0; bottom:-1px;
  height:1px; background:rgba(0,0,0,.18) }
/* when the slim article header is pinned, the table header parks beneath it */
body.readbar-on .mj-tbl--sticky thead th{ top:var(--mj-stickyhead-h,52px) }

/* =============================================================================
   ARTICLE SINGLE — 🧪 PRODUCT REVIEW  (.mj-rv*)
   REWORKED 2026-07-30. The first version led with a score CARD: a bordered panel,
   a 3.2rem numeral and four coloured progress bars. That is the visual signature of a
   template affiliate site, and it announced "this page exists to rate a product" before
   the reader had read a word.

   An editorial review leads with the WRITING. The score is an outcome of the argument,
   not the headline — so it now sits BELOW the hero as a quiet, typographic verdict bar:
   the numeral set in the display face at reading scale, the axes as a plain inline list,
   no progress bars, no fill, one hairline frame. Forbes and The Verge both do this: the
   title carries the page, the score is a fact you meet on the way in.
   ============================================================================= */
.mj-rv-hero{ margin:var(--sp-6) 0 var(--sp-5) }
.mj-rv-hero__kicker{ display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin:0 0 var(--sp-3); font-family:var(--font-meta); font-size:.7rem; letter-spacing:2px;
  text-transform:uppercase }
.mj-rv-hero__kicker .rv-k{ background:var(--mj-ink-900); color:#fff; padding:3px 9px;
  border-radius:2px; font-weight:700 }
.mj-rv-hero__kicker .rv-c{ color:var(--mj-text-muted); font-weight:700 }
.mj-rv-hero h1{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(2rem,4.6vw,3.1rem); line-height:1.05; letter-spacing:-.018em;
  margin:0 0 var(--sp-4); color:var(--mj-black); max-width:20ch }
body.dark-mode .mj-rv-hero h1{ color:#fff }
.mj-rv-hero__dek{ font-size:1.18rem; line-height:1.55; color:var(--mj-text-muted);
  margin:0; max-width:56ch }

/* THE VERDICT BAR — quiet, typographic, one hairline frame. Not a dashboard. */
.mj-rv-verdict{ display:grid; grid-template-columns:auto 1px 1fr auto; gap:var(--sp-5);
  align-items:center; margin:var(--sp-6) 0; padding:var(--sp-5) 0;
  border-top:2px solid var(--mj-black); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-rv-verdict{ border-top-color:#fff; border-bottom-color:#2a2a2d }
.mj-rv-verdict__score{ display:flex; align-items:baseline; gap:5px;
  font-family:var(--font-display); font-weight:800; color:var(--mj-black); line-height:1 }
body.dark-mode .mj-rv-verdict__score{ color:#fff }
.mj-rv-verdict__score b{ font-size:2.5rem; letter-spacing:-.03em }
.mj-rv-verdict__score span{ font-size:.9rem; color:var(--mj-gray-400); font-weight:700 }
.mj-rv-verdict__score em{ display:block; font-style:normal; font-family:var(--font-meta);
  font-size:.56rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-gray-400);
  margin-top:5px; font-weight:700 }
.mj-rv-verdict__rule{ align-self:stretch; background:var(--mj-rule) }
body.dark-mode .mj-rv-verdict__rule{ background:#2a2a2d }
/* the axes: a plain inline list. Weight and a single tone, no bars, no palette. */
.mj-rv-axes{ display:flex; flex-wrap:wrap; gap:var(--sp-3) var(--sp-5); margin:0; padding:0;
  list-style:none }
.mj-rv-axes li{ font-size:.82rem; color:var(--mj-text-muted); white-space:nowrap }
.mj-rv-axes b{ font-family:var(--font-display); font-weight:800; color:var(--mj-black);
  margin-left:5px }
body.dark-mode .mj-rv-axes b{ color:#fff }
.mj-rv-axes li.is-low b{ color:var(--mj-alert) }      /* only the weak axis takes colour */
body.dark-mode .mj-rv-axes li.is-low b{ color:#ff8b93 }
.mj-rv-verdict__buy{ text-align:right }
.mj-rv-verdict__price{ display:block; font-family:var(--font-display); font-weight:800;
  font-size:1.4rem; color:var(--mj-black); line-height:1; margin-bottom:3px }
body.dark-mode .mj-rv-verdict__price{ color:#fff }
.mj-rv-verdict__note{ display:block; font-size:.74rem; color:var(--mj-gray-400);
  margin-bottom:10px }

@media(max-width:820px){
  .mj-rv-verdict{ grid-template-columns:auto 1fr; gap:var(--sp-4) var(--sp-5) }
  .mj-rv-verdict__rule{ display:none }
  .mj-rv-verdict__buy{ grid-column:1/-1; text-align:left;
    padding-top:var(--sp-4); border-top:1px solid var(--mj-rule) }
  body.dark-mode .mj-rv-verdict__buy{ border-top-color:#2a2a2d }
}

/* who-it-fits split, unchanged */
.mj-rv-fit{ display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-5);
  margin:var(--sp-6) 0; padding:var(--sp-5) 0;
  border-top:2px solid var(--mj-black); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-rv-fit{ border-top-color:#fff; border-bottom-color:#2a2a2d }
.mj-rv-fit__k{ display:flex; align-items:center; gap:8px; margin:0 0 8px;
  font-family:var(--font-meta); font-size:.66rem; letter-spacing:1.6px; text-transform:uppercase;
  font-weight:700 }
.mj-rv-fit__k--for{ color:#08694c } .mj-rv-fit__k--not{ color:var(--mj-alert) }
.mj-rv-fit p{ margin:0; font-size:.98rem; line-height:1.6; color:var(--mj-ink) }
body.dark-mode .mj-rv-fit p{ color:#e8e8ea }
@media(max-width:560px){ .mj-rv-fit{ grid-template-columns:1fr; gap:var(--sp-4) } }

/* =============================================================================
   ARTICLE SINGLE — 🏪 BUSINESS GUIDE  (.mj-bg*)
   Persona-led, not product-led. The reader arrives as "a salon owner", not as
   someone shopping a category, so the hero states THEIR problem before naming a
   product. Distinct from List (index) and Product Review (argument about one thing):
   this is ADVICE, and advice opens with what the reader is up against.
   ============================================================================= */
.mj-bg-hero{ margin:var(--sp-6) 0 var(--sp-7) }
.mj-bg-hero__kicker{ display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin:0 0 var(--sp-3); font-family:var(--font-meta); font-size:.7rem; letter-spacing:2px;
  text-transform:uppercase }
.mj-bg-hero__kicker .bg-k{ background:var(--mj-red); color:#fff; padding:3px 9px;
  border-radius:2px; font-weight:700 }
.mj-bg-hero__kicker .bg-c{ color:var(--mj-text-muted); font-weight:700 }
.mj-bg-hero h1{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(1.9rem,4.2vw,2.85rem); line-height:1.08; letter-spacing:-.015em;
  margin:0 0 var(--sp-4); color:var(--mj-black); max-width:16ch }
body.dark-mode .mj-bg-hero h1{ color:#fff }
.mj-bg-hero__dek{ font-size:1.16rem; line-height:1.6; color:var(--mj-text-muted);
  margin:0; max-width:52ch }

/* THE REQUIREMENTS PANEL — the unit that makes this type work.
   "What a salon actually needs" stated up front, so every later recommendation is
   measured against it rather than asserted. Reusable across all 28 verticals. */
.mj-req{ margin:var(--sp-6) 0; border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  background:var(--mj-surface); overflow:hidden }
body.dark-mode .mj-req{ background:#131315; border-color:#2a2a2d }
.mj-req__head{ display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-4); background:var(--mj-red); color:#fff;
  font-family:var(--font-meta); font-size:.68rem; letter-spacing:1.6px; text-transform:uppercase;
  font-weight:700 }
.mj-req__head .mj-sq{ background:#fff }
.mj-req__head .mj-attrib{ color:#fff; opacity:.9 }
.mj-req__grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)) }
.mj-req__item{ padding:var(--sp-5) var(--sp-4); border-right:1px solid var(--mj-rule);
  transition:background .16s ease }
.mj-req__item:last-child{ border-right:0 }
body.dark-mode .mj-req__item{ border-right-color:#2a2a2d }
.mj-req__item:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-req__item:hover{ background:#1b1b1e }
.mj-req__n{ display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px;
  border-radius:2px; background:var(--mj-ink-900); color:#fff; font-family:var(--font-display);
  font-weight:800; font-size:.72rem; margin-bottom:10px }
body.dark-mode .mj-req__n{ background:#fff; color:#0a0a0a }
.mj-req__t{ font-family:var(--font-display); font-weight:800; font-size:1rem; margin:0 0 6px;
  color:var(--mj-black); line-height:1.25 }
body.dark-mode .mj-req__t{ color:#fff }
.mj-req__d{ font-size:.9rem; line-height:1.55; color:var(--mj-text-muted); margin:0 }
.mj-req__why{ display:block; margin-top:8px; font-family:var(--font-meta); font-size:.64rem;
  letter-spacing:1.2px; text-transform:uppercase; font-weight:700; color:#08694c }
body.dark-mode .mj-req__why{ color:#34d8a6 }
@media(max-width:640px){
  .mj-req__grid{ grid-template-columns:1fr }
  .mj-req__item{ border-right:0; border-bottom:1px solid var(--mj-rule) }
  .mj-req__item:last-child{ border-bottom:0 }
  body.dark-mode .mj-req__item{ border-bottom-color:#2a2a2d }
}

/* the "does it clear the bar" checklist — measures picks against .mj-req */
/* THE BAR-CHECK IS ONE ELEMENT (2026-08-27), not a table plus two loose
   siblings. Its caption carries the verification date and its hint carries the
   scroll affordance, so all three travel together or the element ships without
   its provenance. The wrapper exists to make that true in extraction. */
.mj-bcheck{ margin:var(--sp-6) 0 }
.mj-bcheck .mj-bar-check{ margin:0 }
/* the scroll floor was `style="min-width:660px"` in the prototype, which L0 correctly
   refuses on a built page: five requirement columns plus a machine name do not fit a
   phone, and how wide they need to be is a library decision, not a markup one. */
/* min-width, not width: inside the 960px breakout the table should use the room it
   has, and only start scrolling below the point where six columns stop fitting. */
.mj-bcheck .mj-bar-check{ min-width:560px }
.mj-bcheck .mj-tbl-cap{ width:auto; margin-left:0; transform:none }
/* six columns of judgement need room to breathe; the machine name carries the most
   text so it gets the slack, and the state cells stay narrow and scannable */
.mj-bcheck .mj-bar-check th:first-child,
.mj-bcheck .mj-bar-check td:first-child{ width:30%; min-width:180px }
.mj-bcheck .mj-bar-check th{ font-size:.7rem; line-height:1.25 }
/* THE PAPER UNDER THE TABLE (2026-09-11, Lucky: "the table is not rendered the way
   prototypes are rendered, notice that there is no white background"). Every other
   table family sits on --mj-surface; the bar-check alone was transparent, so on the
   off-white page it read as unfinished. Mirrors .mj-tw table, light and dark. */
.mj-bar-check{ width:100%; border-collapse:collapse; margin:var(--sp-5) 0; font-size:.95rem;
  background:var(--mj-surface) }
.mj-bar-check th{ text-align:left; padding:var(--sp-3); background:var(--mj-ink-900); color:#fff;
  font-family:var(--font-meta); font-size:.66rem; letter-spacing:1.4px; text-transform:uppercase }
.mj-bar-check td{ padding:var(--sp-3); border-bottom:1px solid var(--mj-rule); vertical-align:top }
body.dark-mode .mj-bar-check{ background:#131315 }
body.dark-mode .mj-bar-check td{ border-bottom-color:#2a2a2d }
.mj-bar-check tbody tr:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-bar-check tbody tr:hover{ background:#1b1b1e }
.mj-bar-check td:first-child{ font-weight:700; color:var(--mj-black) }
body.dark-mode .mj-bar-check td:first-child{ color:#fff }
/* CALMED 2026-07-30. Three saturated colours across a whole table turned a comparison
   into a colour chart — the reader fought the palette instead of reading the answer.
   Now the MARK carries the meaning (✓ / ~ / –) and the text stays near-black; only the
   tick takes colour, because "this clears the bar" is the one thing worth spotting fast. */
.mj-yes,.mj-no,.mj-part{ display:inline-flex; align-items:baseline; gap:7px;
  font-size:.9rem; color:var(--mj-ink); font-weight:500 }
body.dark-mode .mj-yes,body.dark-mode .mj-no,body.dark-mode .mj-part{ color:#e8e8ea }
.mj-yes::before,.mj-no::before,.mj-part::before{ width:11px; flex:0 0 auto; font-weight:800;
  text-align:center; font-size:.95em }
.mj-yes::before{ content:"✓"; color:#08694c }
.mj-part::before{ content:"~"; color:var(--mj-gray-400) }
.mj-no::before{ content:"–"; color:var(--mj-gray-400) }
.mj-yes{ font-weight:700 }
body.dark-mode .mj-yes::before{ color:#34d8a6 }

/* =============================================================================
   MONETIZATION PLACEMENTS — inline-cta + affiliate-banner
   Built 2026-07-30 from the real ACF schema (raw/acf setup/), not invented:
     inline-cta      → style_variant: Default | Outlined | Subtle | Full Width
                       + highlight_text, description, button_text, button_url, urgency_label
     affiliate-banner → banner_variants repeater (image/size/platform/format)
                       + promo_dates{start_date,end_date}, affiliate_link, related_brand
   ============================================================================= */

/* --- inline-cta (CPT: inline-cta) -------------------------------------------
   Sits INSIDE prose, mid-paragraph-flow. Quieter than cta-block (a section) and
   louder than offer_chip (a word). Four style variants, exactly the ACF choices.
   W6/M1: every offer states what the reader GETS — the description is not optional.
   -------------------------------------------------------------------------- */
.mj-ict{ display:grid; grid-template-columns:1fr auto; gap:var(--sp-4); align-items:center;
  margin:var(--sp-5) 0; padding:var(--sp-4) var(--sp-5);
  background:var(--mj-surface); border-left:4px solid var(--mj-red);
  border-radius:0 var(--radius-md) var(--radius-md) 0 }
body.dark-mode .mj-ict{ background:#131315 }
.mj-ict__hl{ display:block; font-family:var(--font-display); font-weight:800; font-size:1.02rem;
  color:var(--mj-black); margin:0 0 3px; line-height:1.3 }
body.dark-mode .mj-ict__hl{ color:#fff }
.mj-ict__d{ margin:0; font-size:.92rem; line-height:1.55; color:var(--mj-text-muted) }
.mj-ict__act{ display:flex; flex-direction:column; align-items:flex-end; gap:6px }
.mj-ict__u{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:var(--mj-red); white-space:nowrap }

/* style_variant = Outlined */
.mj-ict--outlined{ background:transparent; border:1px solid var(--mj-ink-900);
  border-left-width:4px; border-left-color:var(--mj-ink-900); border-radius:var(--radius-md) }
body.dark-mode .mj-ict--outlined{ background:transparent; border-color:#fff; border-left-color:#fff }
/* style_variant = Subtle — the one that must NOT compete with body copy */
.mj-ict--subtle{ background:transparent; border-left:2px solid var(--mj-rule);
  padding:var(--sp-3) var(--sp-4); border-radius:0 }
.mj-ict--subtle .mj-ict__hl{ font-size:.95rem }
.mj-ict--subtle .mj-ict__u{ color:var(--mj-gray-400) }
/* style_variant = Full Width — breaks the reading column, solid brand surface */
.mj-ict--full{ grid-template-columns:1fr; gap:var(--sp-3); text-align:left;
  background:var(--mj-ink-900); border-left:0; border-radius:var(--radius-md);
  padding:var(--sp-5) }
.mj-ict--full .mj-ict__hl{ color:#fff; font-size:1.2rem }
.mj-ict--full .mj-ict__d{ color:rgba(255,255,255,.78) }
.mj-ict--full .mj-ict__act{ align-items:flex-start }
.mj-ict--full .mj-ict__u{ color:#ff8b93 }
@media(max-width:620px){
  .mj-ict{ grid-template-columns:1fr; gap:var(--sp-3) }
  .mj-ict__act{ align-items:flex-start }
}

/* --- affiliate-banner (CPT: affiliate-banner) -------------------------------
   A campaign placement, not an editorial insert. Renders a banner_variants row
   sized to its slot. Carries the disclosure ALWAYS, and an expiry ONLY when
   promo_dates.end_date holds a real date (design directive v3 Part 5).
   -------------------------------------------------------------------------- */
.mj-bnr{ position:relative; display:block; margin:var(--sp-6) 0; overflow:hidden;
  border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  background:var(--mj-surface-2); text-decoration:none;
  transition:border-color .16s ease, box-shadow .16s ease }
body.dark-mode .mj-bnr{ background:#141416; border-color:#2a2a2d }
.mj-bnr:hover{ border-color:var(--mj-red); box-shadow:0 6px 22px rgba(0,0,0,.08) }
.mj-bnr:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
.mj-bnr__label{ position:absolute; top:0; left:0; z-index:2;
  font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.4px; text-transform:uppercase;
  font-weight:700; color:#fff; background:rgba(10,10,10,.72); padding:3px 8px;
  border-radius:0 0 2px 0 }
.mj-bnr__img{ display:block; width:100%; height:auto }
.mj-bnr__fallback{ display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:8px; min-height:120px; padding:var(--sp-5); text-align:center }
.mj-bnr__fallback .b{ font-family:var(--font-display); font-weight:800; font-size:1.05rem;
  color:var(--mj-black) }
body.dark-mode .mj-bnr__fallback .b{ color:#fff }
.mj-bnr__fallback .s{ font-size:.88rem; color:var(--mj-text-muted) }
.mj-bnr__foot{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
  padding:8px var(--sp-4); border-top:1px solid var(--mj-rule);
  font-family:var(--font-meta); font-size:.64rem; letter-spacing:.6px; color:var(--mj-gray-400) }
body.dark-mode .mj-bnr__foot{ border-top-color:#2a2a2d }
/* the honest expiry — renders ONLY from a real promo_dates.end_date (v3 Part 5) */
.mj-bnr__ends{ margin-left:auto; font-weight:700; color:var(--mj-red); text-transform:uppercase;
  letter-spacing:1.2px }
.mj-bnr__ends--soon{ background:var(--mj-alert-subtle); color:var(--mj-alert);
  padding:2px 7px; border-radius:2px }
/* slot sizes, from banner_variants.size */
.mj-bnr--leaderboard{ max-width:728px } .mj-bnr--rail{ max-width:300px }
.mj-bnr--inline{ max-width:var(--article-width) } .mj-bnr--wide{ max-width:var(--wide) }

/* =============================================================================
   smart-visual-block (CPT: smart-visual-block)  — .mj-vb*
   Built from the REAL ACF group "🖼️ Visual Block – Core Writing (SmartCMS v4)":
     block_type   SELECT: ⚖️ Comparison | 🪜 Step-by-Step Guide | 🎞️ Image Carousel
                          | 🔳 Visual Grid | ⚔️ A vs B
     block_items  REPEATER: item_image · item_label · item_value · item_description · item_alt_text
     display_style SELECT: Default | Boxed | Grid Layout | Carousel
     block_intro / block_outro (wysiwyg)
   This is the Explainer's signature unit: it turns "how a thing works" from three
   paragraphs into a scannable structure, which is the whole GATE R5 trade.
   ============================================================================= */
.mj-vb{ margin:var(--sp-6) 0 }
.mj-vb__intro,.mj-vb__outro{ font-size:.98rem; line-height:1.7; color:var(--mj-text-muted);
  margin:0 0 var(--sp-4) }
.mj-vb__outro{ margin:var(--sp-4) 0 0 }
.mj-vb__head{ display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-3) var(--sp-4);
  background:var(--mj-ink-900); color:#fff; font-family:var(--font-meta); font-size:.68rem;
  letter-spacing:1.6px; text-transform:uppercase; font-weight:700;
  border-radius:var(--radius-md) var(--radius-md) 0 0 }
.mj-vb__head .mj-attrib{ color:#fff; opacity:.85 }

/* block_type = 🪜 Step-by-Step Guide — numbered flow, the explainer workhorse */
.mj-vb--steps .mj-vb__items{ border:1px solid var(--mj-rule); border-top:0;
  border-radius:0 0 var(--radius-md) var(--radius-md); background:var(--mj-surface); overflow:hidden }
body.dark-mode .mj-vb--steps .mj-vb__items{ background:#131315; border-color:#2a2a2d }
.mj-vb--steps .mj-vb__item{ display:grid; grid-template-columns:auto 1fr auto; gap:var(--sp-4);
  align-items:baseline; padding:var(--sp-4) var(--sp-5); border-top:1px solid var(--mj-rule);
  transition:background .16s ease }
.mj-vb--steps .mj-vb__item:first-child{ border-top:0 }
body.dark-mode .mj-vb--steps .mj-vb__item{ border-top-color:#2a2a2d }
.mj-vb--steps .mj-vb__item:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-vb--steps .mj-vb__item:hover{ background:#1b1b1e }
/* ── THE MARKER MAY BE A GLYPH · observations 20.1 and 18 ────────────────
   `.mj-vb--steps` numbers its items, which is right for a sequence — first,
   second, third. But Lucky liked something else, from the navblock list:

       "I like the relevant emojis used as a way to make the block feel a bit
        less boring or to emphasise good read."                       — obs 20.1

   A glyph and a number answer different questions. A NUMBER says these come in
   an order and you should follow it. A GLYPH says these are four separate things
   and here is what each is about. Using a number where there is no order is a
   small lie about the content, and it is the reason a list of unrelated points
   reads as a procedure the reader has failed to start.

   So the marker is data — `1` or `💸` — and the shape follows it: a number keeps
   the solid counter it has always had, a glyph drops the disc and stands as
   itself, because a coloured circle around an emoji is two markers doing one job. */
/* THE FACT IS `data-marker`; THE SHAPE IS THIS RULE. Carrying `mj-mark--glyph`
   as a slotted class put the choice of appearance inside the renderer — the same
   law that split the reveal tray and moved the table's tick to `data-mark`.
   A glyph drops the solid disc and stands as itself: a coloured circle around an
   emoji is two markers doing one job. */
.mj-num[data-marker="glyph"]{ background:none; color:inherit; font-size:1.05rem;
  box-shadow:none }
body.dark-mode .mj-num[data-marker="glyph"]{ background:none; color:inherit }

.mj-vb__lab{ font-family:var(--font-display); font-weight:800; font-size:1rem; color:var(--mj-black);
  margin:0 0 3px }
body.dark-mode .mj-vb__lab{ color:#fff }
.mj-vb__desc{ margin:0; font-size:.92rem; line-height:1.6; color:var(--mj-text-muted) }
.mj-vb__val{ font-family:var(--font-display); font-weight:800; font-size:1.05rem;
  color:var(--mj-black); white-space:nowrap }
body.dark-mode .mj-vb__val{ color:#fff }

/* block_type = 🔳 Visual Grid + display_style = Grid Layout */
.mj-vb--grid .mj-vb__items{ display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
  border:1px solid var(--mj-rule); border-top:0;
  border-radius:0 0 var(--radius-md) var(--radius-md); overflow:hidden; background:var(--mj-surface) }
body.dark-mode .mj-vb--grid .mj-vb__items{ background:#131315; border-color:#2a2a2d }
.mj-vb--grid .mj-vb__item{ padding:var(--sp-5) var(--sp-4); border-right:1px solid var(--mj-rule);
  transition:background .16s ease }
.mj-vb--grid .mj-vb__item:last-child{ border-right:0 }
body.dark-mode .mj-vb--grid .mj-vb__item{ border-right-color:#2a2a2d }
.mj-vb--grid .mj-vb__item:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-vb--grid .mj-vb__item:hover{ background:#1b1b1e }
.mj-vb--grid .mj-vb__val{ display:block; font-size:1.9rem; line-height:1; margin-bottom:8px }

/* the proportion bar — where a number actually goes. Explainer's clearest device. */
.mj-vb--split .mj-vb__bar{ display:flex; height:34px; overflow:hidden;
  border-radius:0 0 var(--radius-md) var(--radius-md) }
.mj-vb__seg{ display:flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; font-size:.78rem; color:#fff;
  transition:filter .16s ease; min-width:0; flex:0 0 var(--seg,auto) }
.mj-vb__seg:hover{ filter:brightness(1.12) }
.mj-vb__seg--a{ background:var(--mj-ink-900) }
.mj-vb__seg--b{ background:var(--mj-trust) }
.mj-vb__seg--c{ background:var(--mj-red) }
/* ── THE PAIR PICK · `.mj-pair` ───────────────────────────────────────────
   Design authority, Opus 5, 2026-08-20. Owner observation 2.1.3.

       "how about if someone is already an established business and they already
        understand the concept that neither company is an enemy, and buying two
        to use interchangeably could help … I would have segmented my verdict
        into two parts: can afford one or one option will do, and can afford two.
        Not as a mere suggestion, but with real practical paint out of where the
        benefit actually lies."

   Every segment of a verdict answers "which ONE". This answers "both, and here
   is the job each one does" — which for a counter already selling airtime is not
   a compromise, it is the cheaper answer.

   IT IS THE ONLY ELEMENT ON THE SITE THAT RECOMMENDS SPENDING MORE, so it carries
   the heaviest proof burden: the two machines as real product boxes at real
   prices, an explicit division of labour, and a total whose verification is
   stated rather than assumed. A TOTAL IS ONLY AS VERIFIED AS ITS LEAST-VERIFIED
   PART, and the renderer says so when one of them is pending.

   Built from the pieces that already exist — the compact product card the tray
   uses, the library's own ink panel for the total. */
.mj-pair{ margin:var(--sp-7) 0; border:1px solid var(--mj-rule);
  background:var(--mj-surface) }
body.dark-mode .mj-pair{ background:#131315; border-color:#2a2a2d }
.mj-pair__head{ padding:var(--sp-4) var(--sp-5) 0 }
.mj-pair__k{ display:block; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:1.6px; text-transform:uppercase; font-weight:700;
  color:var(--mj-growth-text); margin-bottom:6px }
.mj-pair__h{ margin:0; font-family:var(--font-display); font-size:1.24rem;
  line-height:1.25; color:var(--mj-black) }
body.dark-mode .mj-pair__h{ color:#fff }
.mj-pair__why{ margin:var(--sp-3) 0 0; padding:0 var(--sp-5);
  font-size:.94rem; line-height:1.65; color:var(--mj-text-muted) }
/* ONE COLUMN, ALWAYS. This grid used to be 2-up, which put a horizontal slim
   row — image | name | why | price | action, five things on one line — into a
   290px column at every desktop width. The row did not break loudly; it wrapped
   its hook one word per line and slid the price under the product shot, so the
   block still "rendered" and the gates still passed.

   The row is right and the grid was wrong. r_product_box's own note says the
   slim row serves "a tray, an inline reveal or a pair", and it is the form
   Lucky approved in the reveal: full measure, about 110px each, five facts on
   a line. A pair is two of those stacked, not two of them crushed side by side.
   The 640px override that used to rescue this on phones is now the base. */
.mj-pair__cards{ display:grid; grid-template-columns:1fr;
  gap:var(--sp-4); padding:var(--sp-5); align-items:start }
.mj-pair__cards .mj-pb{ width:100%; max-width:none; margin:0; transform:none;
  box-shadow:none }

/* THE DIVISION OF LABOUR IS THE ARGUMENT. Two machines with no stated jobs is
   an upsell; two machines with a job each is a plan. */
.mj-pair__roles{ display:grid; gap:0; margin:0 var(--sp-5) var(--sp-5);
  border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-pair__roles{ border-top-color:#2a2a2d }
.mj-pair__role{ display:grid; grid-template-columns:minmax(5rem,auto) minmax(0,1fr);
  gap:var(--sp-4); margin:0; padding:11px 0; font-size:.9rem; line-height:1.6;
  border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-pair__role{ border-bottom-color:#2a2a2d }
.mj-pair__role:last-child{ border-bottom:0 }
.mj-pair__who{ font-family:var(--font-display); font-weight:700;
  color:var(--mj-black) }
.mj-pair__job{ color:var(--mj-text-muted) }
body.dark-mode .mj-pair__who{ color:#fff }

.mj-pair__total{ display:grid; grid-template-columns:minmax(0,1fr) auto;
  align-items:center; gap:var(--sp-3) var(--sp-5);
  padding:var(--sp-4) var(--sp-5); background:var(--mj-ink-900); color:#fff }
.mj-pair__total-k{ font-family:var(--font-meta); font-size:.64rem; letter-spacing:1.5px;
  text-transform:uppercase; font-weight:700; color:rgba(255,255,255,.8) }
.mj-pair__total-v{ font-family:var(--font-display); font-size:1.5rem; font-weight:800;
  grid-row:span 2; align-self:center; font-variant-numeric:tabular-nums }
.mj-pair__total-n{ grid-column:1; font-size:.78rem; line-height:1.5;
  color:rgba(255,255,255,.72) }
@media(max-width:640px){
  /* .mj-pair__cards is 1fr at every width now — the override that lived here
     said the same thing twice. */
  .mj-pair__role{ grid-template-columns:1fr; gap:2px }
}

/* ── A PRICE A READER CANNOT ACT ON IS A DEAD END ─────────────────────────
   Lucky: "the price entity needs a buy button, or it should just be a
   horizontal product box."

   He is right, and the reason is that this element states the most decision-ready
   fact on the page — a dated price — and then asked the reader to go and find the
   action somewhere else. The foot is optional: an entity used purely as a price
   RECORD (history, multi-country comparison) has no single thing to buy and
   renders without it. The note is deliberate — the amount above is the last
   figure we checked, and this element must never imply a live feed. */
.mj-mz-price__foot{ display:flex; align-items:center; gap:var(--sp-4);
  flex-wrap:wrap; padding:var(--sp-4) var(--sp-5);
  border-top:1px solid var(--mj-rule) }
.mj-mz-price__cta{ display:inline-flex; align-items:center;
  font-family:var(--font-display); font-size:.78rem; letter-spacing:.6px;
  text-transform:uppercase; text-decoration:none; color:#fff;
  background:var(--mj-red); padding:11px var(--sp-5);
  border-radius:var(--radius-sm); transition:background .15s ease }
.mj-mz-price__cta:hover{ background:var(--mj-red-dark, #c31b20) }
.mj-mz-price__note{ font-family:var(--font-meta); font-size:.66rem;
  letter-spacing:.2px; line-height:1.45; color:var(--mj-gray-400); flex:1 1 16ch }
body.dark-mode .mj-mz-price__foot{ border-top-color:#2a2a2d }

/* ── ACROSS THE DESK · `.mj-desk` ─────────────────────────────────────────
   Lucky asked for "a trending section like Forbes — compact, rotating".

   THE FORM IS FORBES'S; THE LABEL IS NOT, AND THAT IS DELIBERATE. Forbes can
   say "trending" because it counts millions of reads. This desk has no
   analytics wired, so a "trending" or "most read" heading here would be a
   traffic claim invented to look busy — on a site whose whole argument is that
   every number carries a date and a source. It is the one dishonest thing the
   homepage would contain. So the block keeps what actually makes that module
   work — density, numbering, fast routing — under a heading that is true.

   Denser than "Start here" on purpose: number and title, no dek. Start here
   SELLS three routes to a new reader; this ROUTES a reader who is already
   moving, which is a different job and needs a fifth of the height per item.

   When real popularity data exists, this element takes it unchanged — only the
   pool and the heading change, and the heading may finally say "most read". */
.mj-desk{ display:grid; grid-template-columns:repeat(auto-fit,minmax(232px,1fr));
  gap:0 var(--sp-5); margin:var(--sp-4) 0 0;
  border-top:1px solid var(--mj-rule) }
.mj-desk__i{ display:flex; align-items:flex-start; gap:var(--sp-4);
  padding:var(--sp-4) 0; text-decoration:none;
  border-bottom:1px solid var(--mj-rule) }
/* ── THE NEWSROOM BLOCK · .mj-hp-news (2026-09-20) ───────────────────────────
   The homepage's main section, built to the shape Lucky picked from the CITY
   demo's section front: a three-up grid of story cards, and a ranked rail beside
   it. Everything in it is an existing card — `.mj-nl-sub` from the news archive
   and `.mj-desk` from this page — so the block is an arrangement, not a new
   vocabulary. The rail's rows now carry a thumbnail, which is also what makes
   "Latest across the desk" and every vertical's closing rail look finished the
   day the pictures land. */
.mj-hp-news{ display:grid; grid-template-columns:minmax(0,1fr) 316px; gap:var(--sp-7);
  align-items:start; margin:0 0 var(--sp-7) }
.mj-hp-news__m{ min-width:0 }
.mj-hp-news__m .mj-nl-subs{ grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--sp-5) var(--sp-6) }
.mj-hp-news__m .mj-nl-subs + .mj-nl-subs{ margin-top:var(--sp-5); padding-top:var(--sp-5);
  border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-hp-news__m .mj-nl-subs + .mj-nl-subs{ border-top-color:#2a2a2d }
.mj-hp-news__s{ min-width:0; padding-left:var(--sp-6); border-left:1px solid var(--mj-rule) }
body.dark-mode .mj-hp-news__s{ border-left-color:#2a2a2d }
.mj-hp-news__sh{ margin:0 0 var(--sp-2); font-family:var(--font-meta); font-size:.6rem;
  font-weight:700; letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-text-muted) }
.mj-hp-news__s .mj-desk{ grid-template-columns:1fr; gap:0; margin:0 }
.mj-hp-news__more{ display:inline-block; margin-top:var(--sp-4); font-family:var(--font-meta);
  font-size:.64rem; font-weight:700; letter-spacing:.6px; text-transform:uppercase;
  color:var(--mj-trust); text-decoration:none }
.mj-hp-news__more:hover{ text-decoration:underline }
/* THE RAIL'S THUMBNAIL. Small, square-ish, and absent entirely when the article
   has no picture — the row then reads exactly as it did before. */
.mj-desk__th{ flex:0 0 auto; width:64px; height:48px; overflow:hidden; border-radius:2px;
  background:var(--mj-surface-2, #f1f1f3) }
.mj-desk__th:empty{ display:none }
@media(max-width:1060px){
  .mj-hp-news{ grid-template-columns:1fr; gap:var(--sp-6) }
  .mj-hp-news__s{ padding-left:0; border-left:0; border-top:1px solid var(--mj-rule);
    padding-top:var(--sp-5) }
  .mj-hp-news__s .mj-desk{ grid-template-columns:repeat(2,1fr); column-gap:var(--sp-5) }
}
@media(max-width:700px){
  .mj-hp-news__m .mj-nl-subs{ grid-template-columns:repeat(2,minmax(0,1fr)) }
  .mj-hp-news__s .mj-desk{ grid-template-columns:1fr }
}
@media(max-width:460px){ .mj-hp-news__m .mj-nl-subs{ grid-template-columns:1fr } }

/* THE LATEST STRIP: TEN STORIES, AND ALWAYS WHOLE ROWS (Lucky, 2026-09-20: "we don't only have six
   articles, but we have enough to fill up that entire section … in all the other
   view types we have enough to balance two rows").

   The count is fixed in the markup at ten and the COLUMNS are pinned per width,
   because auto-fit can land on three and three never divides ten. Five across
   shows all ten; four across shows eight; two and one across show all ten. No
   width can draw a half-empty last row. */
@media(min-width:1180px){ .mj-latest .mj-desk{ grid-template-columns:repeat(5,1fr) } }
@media(min-width:900px) and (max-width:1179px){
  .mj-latest .mj-desk{ grid-template-columns:repeat(4,1fr) }
  .mj-latest .mj-desk__i:nth-child(n+9){ display:none }
}
@media(min-width:600px) and (max-width:899px){ .mj-latest .mj-desk{ grid-template-columns:repeat(2,1fr) } }
@media(max-width:599px){ .mj-latest .mj-desk{ grid-template-columns:1fr } }
.mj-desk__n{ font-family:var(--font-display); font-size:1.35rem; font-weight:800;
  line-height:1; color:var(--mj-rule-strong,#d8d8d8); flex:0 0 auto;
  font-variant-numeric:tabular-nums }
.mj-desk__i:hover .mj-desk__n{ color:var(--mj-red) }
.mj-desk__b{ display:flex; flex-direction:column; gap:3px; min-width:0 }
.mj-desk__k{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:.7px;
  text-transform:uppercase; color:var(--mj-growth-text) }
.mj-desk__t{ font-family:var(--font-display); font-size:.92rem; line-height:1.3;
  color:var(--mj-black) }
.mj-desk__i:hover .mj-desk__t{ color:var(--mj-red) }
body.dark-mode .mj-desk,
body.dark-mode .mj-desk__i{ border-color:#2a2a2d }
body.dark-mode .mj-desk__n{ color:#3a3a3f }
body.dark-mode .mj-desk__t{ color:#f0f0f2 }

/* ── SETTLEMENT DAYS · `.mj-vb--days` ─────────────────────────────────────
   Design authority, Opus 5, 2026-08-20. Owner observation 2.1.2.

       "I would have added quick table or visual block [the thinner the better]
        to show the payout times … the insert would have been PURELY INFO BASED,
        there is no need to try to sell here, or at all, because knowing which
        one is quicker in settling funds doesn't really have that much influence
        in buying. It can just help prepare someone to know what to expect."

   So this variant has no CTA, no price, no affiliate anything — the only element
   in the body library that is deliberately barred from selling. It answers one
   question: when does the money land, and what does going faster cost.

   A THIN VARIANT OF THE VISUAL BLOCK, not a new element. `.mj-vb--split` is the
   proportion device (one bar, many segments); this is the comparison device
   (many rows, one shared scale). Same head, same attribution mark, same ground.

   THE SHARED SCALE IS THE POINT. Two windows written as "1-2 days" and
   "1-3 days" read almost identically in prose. Drawn against the same axis, the
   difference is a shape, and the same-day tag beside it carries what it costs —
   which is the only part of settlement that changes a decision. */
.mj-vb--days .mj-vb__body{ padding:var(--sp-4) var(--sp-4) var(--sp-3);
  border:1px solid var(--mj-rule); border-top:0 }
body.dark-mode .mj-vb--days .mj-vb__body{ border-color:#2a2a2d }
.mj-vb__days{ list-style:none; margin:0; padding:0; display:grid; gap:9px }
.mj-vb__day{ display:grid; grid-template-columns:minmax(5.5rem,auto) minmax(0,1fr) auto;
  align-items:center; gap:var(--sp-4) }
.mj-vb__dayname{ font-family:var(--font-display); font-weight:700; font-size:.88rem;
  color:var(--mj-black) }
body.dark-mode .mj-vb__dayname{ color:#fff }
.mj-vb__track{ position:relative; display:block; height:10px;
  background:var(--mj-gray-200); border-radius:var(--radius-sm) }
body.dark-mode .mj-vb__track{ background:#2a2a2d }
.mj-vb__span{ position:absolute; top:0; bottom:0; left:var(--from,0%); right:var(--to,0%);
  background:var(--mj-ink-900); border-radius:var(--radius-sm) }
body.dark-mode .mj-vb__span{ background:#e7e7ea }
.mj-vb__dayval{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:1px;
  text-transform:uppercase; font-weight:700; color:var(--mj-text-muted);
  white-space:nowrap }
/* what going faster costs — the only figure here, because it is the only one
   that changes an answer. Green when it is free, red when it is a slice. */
.mj-vb__fast{ display:inline-block; margin-left:8px; padding:2px 7px;
  border-radius:var(--radius-sm); font-size:.6rem; letter-spacing:.8px }
/* THE FACT IS DATA; THE COLOUR IS A DESIGN DECISION AND STAYS HERE.
   Carrying `--free`/`--paid` as a slotted class would have put the choice of
   colour inside the renderer — the same law that split the reveal tray into two
   templates. `data-free` is what the ledger knows; green and red are what this
   library decides that means. */
.mj-vb__fast[data-free="yes"]{ background:var(--mj-growth-subtle); color:var(--mj-growth-text) }
.mj-vb__fast[data-free="no"]{ background:#FDECEC; color:#B3181C }
body.dark-mode .mj-vb__fast[data-free="yes"]{ background:#12261f; color:#4EC98A }
body.dark-mode .mj-vb__fast[data-free="no"]{ background:#2a1416; color:#ff8b8f }
/* the axis, stated once under the rows */
.mj-vb__scale{ display:grid; grid-template-columns:repeat(4,1fr); margin:var(--sp-3) 0 0;
  padding-top:6px; border-top:1px solid var(--mj-rule);
  font-family:var(--font-meta); font-size:.56rem; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-gray-400) }
body.dark-mode .mj-vb__scale{ border-top-color:#2a2a2d }
.mj-vb__scale span:not(:first-child){ text-align:center }
.mj-vb__scale span:last-child{ text-align:right }
@media(max-width:560px){
  .mj-vb__day{ grid-template-columns:1fr auto }
  .mj-vb__track{ grid-column:1 / -1; order:3 }
}
/* A LONG PAYOUT NOTE MAY NOT WIDEN THE PAGE (Phase 9 render audit, 2026-09-13).
   `.mj-vb__dayval` is nowrap, and iKhokha's fast-payout tag ("same day, twice, on
   Nedbank or the iK Debit Card") made one row 634px wide on a 390px phone, so
   yoco-vs-ikhokha, payout-times and capitec-vs-yoco scrolled sideways. On a phone
   the value wraps under the name and the tag takes its own line. */
.mj-vb__days{ grid-template-columns:minmax(0,1fr) }
@media(max-width:560px){
  .mj-vb__day{ grid-template-columns:minmax(0,1fr) }
  .mj-vb__dayval{ white-space:normal; order:2 }
  .mj-vb__fast{ display:table; margin:4px 0 0; white-space:normal }
}

.mj-vb__key{ display:flex; flex-wrap:wrap; gap:var(--sp-4); margin:var(--sp-3) 0 0;
  font-size:.86rem; color:var(--mj-text-muted) }
.mj-vb__key span{ display:inline-flex; align-items:center; gap:7px }
.mj-vb__key i{ width:10px; height:10px; border-radius:1px; flex:0 0 auto; font-style:normal }
@media(max-width:620px){
  .mj-vb--steps .mj-vb__item{ grid-template-columns:auto 1fr; gap:var(--sp-3) }
  .mj-vb__val{ grid-column:2; margin-top:4px }
  .mj-vb--grid .mj-vb__item{ border-right:0; border-bottom:1px solid var(--mj-rule) }
  .mj-vb__seg{ font-size:.68rem }
}

/* --- Byline variations (added 2026-07-30 with the author showcase) -----------
   One component, several densities. The canonical .mj-byline is the article default;
   these are the surfaces it also has to serve.
   -------------------------------------------------------------------------- */

/* the mashjoy.com Team avatar — the logomark IS the face. No stock illustration,
   no initials-in-a-square. A desk credit should look like the desk. */
.mj-avatar--team{ background-size:contain; background-repeat:no-repeat; background-position:center;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 110.25 110.25'%3E%3Crect width='110.25' height='110.25' fill='%23ec2227'/%3E%3Cpolygon points='83.11 20.13 82.48 20.13 68.22 50.72 68.22 50.89 68.14 50.89 53.05 83.25 49.8 90.22 64.77 90.22 68.02 83.25 68.22 83.25 68.22 82.83 83.11 50.89 83.11 90.22 96.68 90.22 96.68 50.89 96.68 21.8 96.68 20.13 83.11 20.13' fill='%23fff'/%3E%3Cpath d='M69.7,20.03h-15.67l-1.74,3.02-16.02,27.74-22.71,39.33h15.67c7.68-13.31,15.37-26.62,23.05-39.94v22.84l10.37-22.23,14.34-30.76h-7.3Z' fill='%23fff'/%3E%3C/svg%3E") }
.mj-avatar--team-ink{ background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 110.25 110.25'%3E%3Crect width='110.25' height='110.25' fill='%23000'/%3E%3Cpolygon points='83.11 20.13 82.48 20.13 68.22 50.72 68.22 50.89 68.14 50.89 53.05 83.25 49.8 90.22 64.77 90.22 68.02 83.25 68.22 83.25 68.22 82.83 83.11 50.89 83.11 90.22 96.68 90.22 96.68 50.89 96.68 21.8 96.68 20.13 83.11 20.13' fill='%23fff'/%3E%3Cpath d='M69.7,20.03h-15.67l-1.74,3.02-16.02,27.74-22.71,39.33h15.67c7.68-13.31,15.37-26.62,23.05-39.94v22.84l10.37-22.23,14.34-30.76h-7.3Z' fill='%23fff'/%3E%3C/svg%3E") }

/* COMPACT — archive cards, related rails, search results. Name + role on one line. */
.mj-byline--compact{ padding:0; margin:0; border:0; gap:9px }
.mj-byline--compact .mj-byline__who > .mj-avatar,
.mj-byline--compact .mj-avatar{ width:26px; height:26px }
.mj-byline--compact .mj-byline__name{ font-size:.8rem }
.mj-byline--compact .mj-byline__role{ font-size:.56rem; margin-top:1px }
.mj-byline--compact .mj-byline__second{ display:none }
.mj-byline--compact .mj-byline__meta{ margin-left:auto }

/* STACKED — narrow columns and the sticky rail. Avatar over name. */
.mj-byline--stacked{ flex-direction:column; align-items:flex-start; gap:var(--sp-3) }
.mj-byline--stacked .mj-byline__meta{ margin-left:0 }

/* AVATAR STACK — two or more credits, faces overlapped like a masthead */
.mj-byline__stack{ display:flex; align-items:center; flex:0 0 auto }
.mj-byline__stack .mj-avatar{ margin-right:-9px; box-shadow:0 0 0 2px var(--mj-surface) }
body.dark-mode .mj-byline__stack .mj-avatar{ box-shadow:0 0 0 2px #0f0f11 }
.mj-byline__stack .mj-avatar:last-child{ margin-right:0 }

/* the "and" that joins two credits without a second full block */
.mj-byline__with{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:.9px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400) }
.mj-byline__with a{ color:var(--mj-ink); text-decoration:none;
  border-bottom:1.5px dotted var(--mj-gray-300) }
.mj-byline__with a:hover{ color:var(--mj-trust); border-bottom-color:var(--mj-trust);
  border-bottom-style:solid }
body.dark-mode .mj-byline__with a{ color:#e8e8ea }

/* full author box: the team variant uses the same avatar scale as a person.
   (An earlier override forced 44px here and fought .mj-avatar--lg — the desk and a
   named author should carry equal visual weight at the foot of an article.) */
.mj-authorbox__links{ display:flex; gap:var(--sp-4); margin-top:10px;
  font-family:var(--font-meta); font-size:.62rem; letter-spacing:1px; text-transform:uppercase;
  font-weight:700 }
.mj-authorbox__links a{ color:var(--mj-trust); text-decoration:none;
  border-bottom:1.5px dotted var(--mj-trust) }
.mj-authorbox__links a:hover{ border-bottom-style:solid }

/* =============================================================================
   TABLES — the narrated comparison unit  (.mj-tw*)
   Built 2026-07-30 from "🧱 Core Comparison Table Content (SmartCMS v4)":
     intro_paragraph (wysiwyg)              ← the SETUP
     comparison_products  repeater: product_label · product_reference · inline_cta
     comparison_features  repeater: feature_label · linked_product · price_label
                                    · cta_style (Button|Badge|Inline Text|Tooltip)
                                    · cell_note (wysiwyg) · checkmark (true_false)
     highlight_summary (wysiwyg)            ← the CONSEQUENCE
     final_verdict (wysiwyg)                ← the RULING
     narrative_angle (select)

   THE SCHEMA'S REAL LESSON: a comparison table is not a grid, it is a NARRATED unit —
   setup, evidence, consequence, ruling. That is exactly the Setup→Insert→Consequence
   pattern in smartcms-insert-intelligence.md, already modelled in the fields. A bare
   grid dropped into prose is an incomplete render of this CPT.
   ============================================================================= */
.mj-tw{ margin:var(--sp-6) 0 }
.mj-tw__intro{ font-size:1rem; line-height:1.7; color:var(--mj-ink); margin:0 0 var(--sp-4) }
body.dark-mode .mj-tw__intro{ color:#e8e8ea }
/* the head: black ground, Technor, mark opposite the title (left OR right) */
.mj-tw__head{ display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-4); background:var(--mj-ink-900); color:#fff;
  border-radius:var(--radius-md) var(--radius-md) 0 0 }
.mj-tw__t{ font-family:var(--font-display); font-weight:800; font-size:.92rem;
  letter-spacing:.2px; margin:0; color:#fff }   /* explicit: a <p> inside the head would
  otherwise inherit the page's body colour and go dark on the black ground */
body.dark-mode .mj-tw__t{ color:#fff }
.mj-tw__meta{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:1px;
  text-transform:uppercase; color:rgba(255,255,255,.62); margin-left:var(--sp-3) }
.mj-tw__scroll{ overflow-x:auto; -webkit-overflow-scrolling:touch;
  border:1px solid var(--mj-rule); border-top:0; border-radius:0 0 var(--radius-md) var(--radius-md) }
body.dark-mode .mj-tw__scroll{ border-color:#2a2a2d }
.mj-tw table{ width:100%; border-collapse:collapse; background:var(--mj-surface) }
body.dark-mode .mj-tw table{ background:#131315 }
.mj-tw th{ text-align:left; padding:var(--sp-4); font-family:var(--font-display);
  font-weight:800; font-size:.86rem; color:var(--mj-black);
  border-bottom:2px solid var(--mj-ink-900); white-space:nowrap; background:var(--mj-surface) }
body.dark-mode .mj-tw th{ color:#fff; border-bottom-color:#fff; background:#131315 }
.mj-tw td{ padding:var(--sp-4); border-bottom:1px solid var(--mj-rule); vertical-align:top;
  font-size:.95rem; line-height:1.5 }
body.dark-mode .mj-tw td{ border-bottom-color:#2a2a2d }
.mj-tw tbody tr{ transition:background .16s ease }
.mj-tw tbody tr:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-tw tbody tr:hover{ background:#1b1b1e }
.mj-tw tbody tr:last-child td{ border-bottom:0 }
/* feature_label — the row label carries weight, it is the thing being compared */
.mj-tw td:first-child,.mj-tw th:first-child{ font-weight:700; color:var(--mj-black) }
body.dark-mode .mj-tw td:first-child{ color:#fff }
/* WHERE THE WEIGHT GOES IN A CELL — Lucky 2026-08-23, second pass.

   First pass lightened the whole cell from 700 to 500. Right direction, wrong
   instrument: it made the table quieter without making anything findable, and
   a reader scanning for "what does this actually cost" still had to read every
   word at one weight to find the one number they came for.

   So the weight moves ONTO the figure and off the sentence around it. The cell
   sentence sits at normal weight and the prices and percentages inside it carry
   700 — in the BODY face, because a second typeface in a table throws the
   column rhythm off and stops looking like a table (tried Technor numerals,
   reverted the same day).

     column 1, the row label ......... 700   the thing being compared
     a figure inside a cell .......... 700   what the reader came to find
     the cell sentence around it ..... 400   context, not emphasis
     the note under a value .......... 400   at .8rem and grey, the qualifier

   `.mj-tw__ul` carries multi-part values — iKhokha's three published tiers read
   as three tiers instead of one comma-spliced run-on. */
/* The review block's slot for the SHARED price element. It owns the spacing;
   the element inside it is `.mj-pb__pay`, the same atom the product box uses,
   so a change to how a price is presented is made once. */
.rb-f-pay{ margin:var(--sp-4) 0 0 }
.rb-f-pay .mj-pb__pay{ margin:0 }
/* ── THE IN-PROSE AFFILIATE LINK · `.mj-alink` [PROMOTED 2026-08-26] ───────
   Lucky: "update the affiliate links element so that it can match the newest
   design. If this is not prototyped, do prototype it so that it works as an
   atomic element all the time."

   It was `.aff`, defined inside ONE prototype's <style> block and scoped to a
   `.a-body` ancestor — so the same link rendered correctly in the article body
   and unstyled inside `.a-pick`, where its dark-mode colour never applied and
   it sat at 1.96:1 against the panel. A rule that depends on its container is
   not an element; it is a coincidence that holds until someone moves it.

   Now: library-level, container-independent, and legible in both themes. The
   red underline says "this link goes somewhere we earn from" without shouting —
   the disclosure at the top of the article does the saying. */
.mj-alink{ color:var(--mj-black); font-weight:700; text-decoration:none;
  border-bottom:1px solid var(--mj-red);
  box-shadow:inset 0 -4px 0 rgba(236,34,39,.16); transition:background .18s ease }
.mj-alink:hover{ background:rgba(236,34,39,.12); color:var(--mj-red) }
.mj-alink:focus-visible{ outline:2px solid var(--mj-red); outline-offset:2px }
body.dark-mode .mj-alink{ color:#fff; box-shadow:inset 0 -4px 0 rgba(236,34,39,.28) }
body.dark-mode .mj-alink:hover{ background:rgba(236,34,39,.22); color:#ff9ba0 }

.mj-tw__fig{ font-weight:700; color:var(--mj-black) }
body.dark-mode .mj-tw__fig{ color:#fff }
/* THE MARKER HAS TO MEAN SOMETHING — the same law as .mj-pc, four hundred
   lines up: "a decorative square says nothing". This library reserves two
   glyphs and neither one fits here. `✓` claims a positive and `–` claims a
   negative, but a tier ladder is NEITHER: "2.65% at R40,000" is not a win or a
   loss, it is one rung. A disc was worse than both — a default, chosen by
   nobody, saying nothing.
   So a table enumeration takes the neutral middot, at gray-400 and small: it
   separates rungs without arguing about them, and it cannot be mistaken for
   the semantic pair a reader has already learned elsewhere on the page.
   Less is more here — Lucky, 2026-08-23. */
.mj-tw__ul{ margin:5px 0 0; padding:0; list-style:none }
.mj-tw__ul li{ position:relative; padding-left:11px; margin:2px 0; line-height:1.45 }
.mj-tw__ul li::before{ content:"·"; position:absolute; left:1px; top:-.06em;
  color:var(--mj-gray-400); font-weight:700 }
body.dark-mode .mj-tw__ul li::before{ color:#6f6f78 }

/* THREE WEIGHTS, THREE JOBS — Lucky 2026-08-23, on the fees article's rate table.
   Every cell's value ships inside a <b>, which the browser renders at 700 — the
   SAME weight as the row label in column one. So a wide table read as a solid
   block of bold, and the column that anchors the row had nothing left to
   distinguish it. The fix is a ladder, not a flattening:

     column 1, the row label ......... 700   the thing being compared
     columns 2..n, the values ........ 500   still clearly the answer
     the note under a value .......... 400   at .8rem, the qualifier

   500 keeps the value heavier than its own footnote and lighter than the label,
   which is the hierarchy the eye needs to read across a row rather than down a
   wall. */
.mj-tw__cell > b{ font-weight:400 }
/* cell_note — the qualifier under a value. Where the honesty lives. */
.mj-tw__note{ display:block; font-size:.8rem; color:var(--mj-gray-400); margin-top:3px;
  font-weight:400 }
/* checkmark (true_false) */
/* PRESENCE AND ABSENCE, MARKED. Owner observation 4.1 asked for icons in tables
   "where appropriate", and the library already had the glyphs — as
   `.mj-tw__yes` / `.mj-tw__no` spans that WRAPPED the value instead of the <b>,
   which is why no table on the site could ever use them without a second cell
   template for every combination of marker and winner.
   The marker is a FACT about the cell, so it travels as data on the cell and the
   stylesheet decides what a tick and a dash look like. Same rule as the
   settlement strip's free/paid tag. */
.mj-tw__cell[data-mark="yes"] > b::before{ content:"✓"; color:#08694c; font-weight:800; margin-right:6px }
.mj-tw__cell[data-mark="no"] > b::before{ content:"–"; color:var(--mj-gray-400); font-weight:800; margin-right:6px }
body.dark-mode .mj-tw__cell[data-mark="yes"] > b::before{ color:#34d8a6 }
.mj-tw__yes::before{ content:"✓"; color:#08694c; font-weight:800; margin-right:6px }
.mj-tw__no::before{ content:"–"; color:var(--mj-gray-400); font-weight:800; margin-right:6px }
body.dark-mode .mj-tw__yes::before{ color:#34d8a6 }
/* the winning column — one tint, never a second colour */
.mj-tw .is-win{ background:var(--mj-growth-subtle) }
body.dark-mode .mj-tw .is-win{ background:rgba(16,185,129,.09) }
.mj-tw tbody tr:hover .is-win{ background:#d8f5ea }
body.dark-mode .mj-tw tbody tr:hover .is-win{ background:rgba(16,185,129,.15) }
/* sticky head for long tables (G11) */
.mj-tw--sticky .mj-tw__scroll{ max-height:70vh; overflow-y:auto }
.mj-tw--sticky thead th{ position:sticky; top:0; z-index:3 }
/* ranked mode — a rank column of squared numerals */
.mj-tw--ranked td:first-child{ width:1%; white-space:nowrap; padding-right:0 }
/* footer: caption + summary + verdict */
/* A CAPTION IS A SENTENCE, NOT A ROW OF ITEMS.
   This was `display:flex` so the scroll hint could be pushed right with
   margin-left:auto — but flex turns every TEXT NODE into its own item. The
   date <b> became one box and the words either side of it became two more,
   each free to wrap on its own and each separated by the container's gap. So a
   caption rendered as "Published provider rates, checked 23 August 2026" and
   then, on a fresh line, a lone full stop followed by the rest of the sentence.
   The hint floats instead: it still sits top-right when there is room, and the
   caption stays one paragraph that wraps like prose. */
.mj-tw__cap{ font-family:var(--font-meta); font-size:.68rem;
  color:var(--mj-gray-400); margin:var(--sp-3) 0 0; line-height:1.6 }
.mj-tw__hint{ float:right; margin-left:var(--sp-4); font-weight:500 }
.mj-tw__summary{ margin:var(--sp-4) 0 0; padding:var(--sp-4) var(--sp-5);
  background:var(--mj-surface-2); border-left:3px solid var(--mj-ink-900);
  border-radius:0 var(--radius-md) var(--radius-md) 0; font-size:.96rem; line-height:1.65 }
body.dark-mode .mj-tw__summary{ background:#1a1a1d; border-left-color:#fff }
.mj-tw__verdict{ margin:var(--sp-3) 0 0; font-size:.96rem; line-height:1.65;
  color:var(--mj-ink) }
body.dark-mode .mj-tw__verdict{ color:#e8e8ea }
.mj-tw__verdict b{ color:var(--mj-black) } body.dark-mode .mj-tw__verdict b{ color:#fff }
@media(max-width:620px){
  .mj-tw th,.mj-tw td{ padding:var(--sp-3) }
  .mj-tw__head{ flex-wrap:wrap } .mj-tw__meta{ margin-left:0; flex-basis:100% }
}

/* =============================================================================
   ARTICLE SINGLE — ⚖️ COMPARISON, 2 PRODUCTS  (.mj-cx*)
   REBUILT 2026-07-30. The previous template was not approved: centred hero (against
   D2), a wider measure than List, and a circular VS badge. Rebuilt rather than patched.

   THE ORGANISING IDEA: a comparison is a FAIR FIGHT.
   List = an index · Product Review = an argument · Business Guide = advice ·
   Explainer = teaching · Comparison = a fair fight, adjudicated.

   Yoco and iKhokha are BOTH sponsors (directive W1), so even-handedness cannot be left
   to the prose — it has to be STRUCTURAL:
     • the hero is symmetric; neither side is larger, first-styled or pre-crowned
     • each product gets an identical review block, same fields in the same order
     • the comparison table tints PER ROW, never a whole column — because a tinted
       column crowns a winner, and the honest answer is that each wins different rows
     • the verdict is segmented: two equal panels, the reader self-selects
   W1 allows Yoco a ~51% earned edge where a tie-break is genuinely warranted. That is
   an editorial nudge, never a visual one.
   ============================================================================= */
.mj-cx-hero{ margin:var(--sp-6) 0 var(--sp-5) }
.mj-cx-hero__kicker{ display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin:0 0 var(--sp-3); font-family:var(--font-meta); font-size:.7rem; letter-spacing:2px;
  text-transform:uppercase }
.mj-cx-hero__kicker .cx-k{ background:var(--mj-ink-900); color:#fff; padding:3px 9px;
  border-radius:2px; font-weight:700 }
.mj-cx-hero__kicker .cx-c{ color:var(--mj-text-muted); font-weight:700 }
.mj-cx-hero h1{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(2rem,4.6vw,3.1rem); line-height:1.05; letter-spacing:-.018em;
  margin:0 0 var(--sp-4); color:var(--mj-black); max-width:20ch }   /* LEFT — D2 */
body.dark-mode .mj-cx-hero h1{ color:#fff }
.mj-cx-hero__dek{ font-size:1.18rem; line-height:1.55; color:var(--mj-text-muted);
  margin:0; max-width:56ch }

/* THE FACE-OFF — symmetric by construction. 1fr auto 1fr can only be even. */
.mj-cx-face{ display:grid; grid-template-columns:1fr auto 1fr; align-items:stretch;
  margin:var(--sp-6) 0; border-top:2px solid var(--mj-black);
  border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-cx-face{ border-top-color:#fff; border-bottom-color:#2a2a2d }
.mj-cx-face__side{ padding:var(--sp-5) var(--sp-5) var(--sp-5) 0 }
.mj-cx-face__side + .mj-cx-face__side{ padding:var(--sp-5) 0 var(--sp-5) var(--sp-5) }
.mj-cx-face__brand{ display:block; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:1.6px; text-transform:uppercase; font-weight:700; margin-bottom:6px }
.mj-cx-face__name{ font-family:var(--font-display); font-weight:800; font-size:1.3rem;
  line-height:1.15; color:var(--mj-black); margin:0 0 8px }
body.dark-mode .mj-cx-face__name{ color:#fff }
.mj-cx-face__price{ font-family:var(--font-display); font-weight:800; font-size:1.6rem;
  color:var(--mj-black); line-height:1; margin:0 0 4px }
body.dark-mode .mj-cx-face__price{ color:#fff }
.mj-cx-face__note{ display:block; font-size:.8rem; color:var(--mj-gray-400); margin-bottom:12px }
.mj-cx-face__for{ font-size:.92rem; line-height:1.55; color:var(--mj-text-muted); margin:0 }
/* the divider carries the VS — a SQUARE, not a circle (G1) */
.mj-cx-face__vs{ display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:10px; padding:var(--sp-5) var(--sp-5) }
.mj-cx-face__vs::before,.mj-cx-face__vs::after{ content:""; flex:1 1 auto; width:1px;
  background:var(--mj-rule); min-height:16px }
body.dark-mode .mj-cx-face__vs::before,body.dark-mode .mj-cx-face__vs::after{ background:#2a2a2d }
.mj-cx-vs{ display:flex; align-items:center; justify-content:center; width:34px; height:34px;
  border-radius:2px; background:var(--mj-ink-900); color:#fff;
  font-family:var(--font-display); font-weight:800; font-size:.78rem; letter-spacing:.5px;
  flex:0 0 auto }
body.dark-mode .mj-cx-vs{ background:#fff; color:#0a0a0a }

/* per-row winner marker for the comparison table — the honest 2-sponsor render.
   A whole tinted COLUMN crowns; a tinted CELL says "this one wins this row". */
.mj-cx-win{ position:relative }
.mj-cx-win::after{ content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--mj-growth) }
.mj-cx-tally{ display:flex; align-items:baseline; gap:var(--sp-4); flex-wrap:wrap;
  margin:var(--sp-3) 0 0; font-family:var(--font-meta); font-size:.66rem; letter-spacing:.8px;
  text-transform:uppercase; color:var(--mj-gray-400) }
.mj-cx-tally b{ font-family:var(--font-display); font-size:.9rem; color:var(--mj-black);
  letter-spacing:0; text-transform:none; margin-left:5px }
body.dark-mode .mj-cx-tally b{ color:#fff }

@media(max-width:720px){
  .mj-cx-face{ grid-template-columns:1fr }
  .mj-cx-face__side{ padding:var(--sp-5) 0 }
  .mj-cx-face__side + .mj-cx-face__side{ padding:var(--sp-5) 0 0;
    border-top:1px solid var(--mj-rule) }
  body.dark-mode .mj-cx-face__side + .mj-cx-face__side{ border-top-color:#2a2a2d }
  .mj-cx-face__vs{ flex-direction:row; padding:0; gap:var(--sp-4) }
  .mj-cx-face__vs::before,.mj-cx-face__vs::after{ width:auto; height:1px; min-height:0;
    flex:1 1 auto }
}

/* the paired review card for ⚖️ Comparison — purpose-built so both sides are
   STRUCTURALLY identical. `.scms-rb--compact` is a horizontal element that sizes to
   its own content, so two of them side by side render at different widths (405 vs 419
   when first tried) — which quietly breaks the fair-fight premise. This one is a plain
   stacked block: equal grid tracks, equal padding, equal everything. */
.mj-cx-card{ display:flex; flex-direction:column; padding:var(--sp-5);
  background:var(--mj-surface); border:1px solid var(--mj-rule);
  border-top:3px solid var(--mj-ink-900); border-radius:0 0 var(--radius-md) var(--radius-md) }
body.dark-mode .mj-cx-card{ background:#131315; border-color:#2a2a2d; border-top-color:#fff }
.mj-cx-card__name{ font-family:var(--font-display); font-weight:800; font-size:1.12rem;
  color:var(--mj-black); margin:0 0 6px; line-height:1.2 }
body.dark-mode .mj-cx-card__name{ color:#fff }
.mj-cx-card__hook{ font-size:.93rem; line-height:1.6; color:var(--mj-text-muted);
  margin:0 0 var(--sp-4) }
.mj-cx-card__specs{ list-style:none; margin:0 0 var(--sp-4); padding:0 }
.mj-cx-card__specs li{ display:flex; justify-content:space-between; gap:var(--sp-3);
  padding:7px 0; border-bottom:1px solid var(--mj-rule); font-size:.9rem }
body.dark-mode .mj-cx-card__specs li{ border-bottom-color:#2a2a2d }
.mj-cx-card__specs li:last-child{ border-bottom:0 }
.mj-cx-card__specs .k{ color:var(--mj-text-muted) }
.mj-cx-card__specs .v{ font-weight:700; color:var(--mj-black); text-align:right }
body.dark-mode .mj-cx-card__specs .v{ color:#fff }
.mj-cx-card__fit{ font-size:.9rem; line-height:1.6; color:var(--mj-text-muted);
  margin:0 0 var(--sp-4) }
.mj-cx-card__fit b{ color:var(--mj-black) } body.dark-mode .mj-cx-card__fit b{ color:#fff }
.mj-cx-card__cta{ margin-top:auto }

/* =============================================================================
   HOMEPAGE + ARCHIVE  (.mj-hp*)
   Layout reference: the Newsreader theme (v3 HA1) — deliberately NOT Forbes, so the
   site does not read as a Forbes/Verge copycat. We take Newsreader's LAYOUT LOGIC
   (mosaic hero, trending rail, full-width section rules) and render it in our own
   system: Technor display, Canva Sans body, square geometry, our tokens.
   ============================================================================= */
.mj-hp-mosaic{ display:grid; grid-template-columns:1.6fr 1fr; gap:2px; margin:0 0 var(--sp-7);
  background:var(--mj-rule) }
body.dark-mode .mj-hp-mosaic{ background:#2a2a2d }
/* AUTO ROWS, NOT TWO FIXED ONES. `1fr 1fr` is right for the two tiles the
   mosaic normally carries and wrong the moment it carries one — a vertical
   with exactly two published articles got a half-height tile above a
   half-height hole. Auto rows give one tile the whole column and two tiles
   half each, which is the same result for the common case and a correct one
   for the thin case. */
.mj-hp-mosaic__side{ display:grid; grid-auto-rows:1fr; gap:2px }
/* a tile — image with the story reversed out over it */
.mj-hp-tile{ position:relative; display:block; overflow:hidden; text-decoration:none;
  background:var(--mj-ink-900); min-height:220px }
.mj-hp-tile--lead{ min-height:460px }
.mj-hp-tile img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  transition:transform .5s cubic-bezier(.2,.6,.2,1) }
.mj-hp-tile:hover img{ transform:scale(1.035) }
.mj-hp-tile::after{ content:""; position:absolute; inset:0;
  background:linear-gradient(to top, rgba(0,0,0,.86) 0%, rgba(0,0,0,.34) 42%, rgba(0,0,0,0) 72%) }
.mj-hp-tile__in{ position:absolute; left:0; right:0; bottom:0; z-index:2;
  padding:var(--sp-5); display:flex; flex-direction:column; gap:8px }
.mj-hp-tile--lead .mj-hp-tile__in{ padding:var(--sp-6) }
.mj-hp-tile__k{ display:inline-flex; align-items:center; gap:6px; align-self:flex-start;
  font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.6px; text-transform:uppercase;
  font-weight:700; color:#fff; background:var(--mj-red); padding:3px 8px; border-radius:2px }
.mj-hp-tile__k--ink{ background:rgba(255,255,255,.16); backdrop-filter:blur(3px) }
.mj-hp-tile__t{ font-family:var(--font-display); font-weight:800; color:#fff; margin:0;
  line-height:1.12; letter-spacing:-.01em; font-size:1.15rem }
.mj-hp-tile--lead .mj-hp-tile__t{ font-size:2.05rem; max-width:18ch }
.mj-hp-tile__m{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:.8px;
  color:rgba(255,255,255,.72); text-transform:uppercase }
.mj-hp-tile:hover .mj-hp-tile__t{ text-decoration:underline; text-underline-offset:3px;
  text-decoration-thickness:2px }
/* placeholder when no image asset exists yet — labelled, never faked */
/* THE PLACEHOLDER MUST NOT COMPETE WITH THE STORY.
   Centred, it sat exactly where the kicker chip and headline sit, and on a
   narrow tile — every tile at 375px — "LEAD IMAGE PENDING" ran straight
   through the RANKED chip. It is a note to the desk about missing art, not
   content, so it goes to the top of the tile and out of the content's way. */
.mj-hp-tile__ph{ position:absolute; inset:0; display:flex; align-items:flex-start;
  justify-content:flex-end; padding:var(--sp-3);
  background:repeating-linear-gradient(45deg,#17171a,#17171a 12px,#1d1d21 12px,#1d1d21 24px);
  font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.4px; text-transform:uppercase;
  color:#55555c }

/* section rule — the full-width divider that paces the page */
/* ONE RULE FOR EVERY SECTION HEAD (2026-09-23). Lucky: "we only have the gray thin
   border as a design consistency throughout". This was a 2px black bar with a blue
   dotted link — the one head on the homepage that did not match the newsroom
   sections above it. Now it is the .mj-nx-head's twin: thin grey rule, the title,
   and a quiet link. */
.mj-hp-rule{ display:flex; flex-wrap:wrap; align-items:baseline; gap:4px var(--sp-3);
  border-top:1px solid var(--mj-rule); padding-top:var(--sp-5); margin:var(--sp-8) 0 var(--sp-4) }
body.dark-mode .mj-hp-rule{ border-top-color:rgba(255,255,255,.14) }
.mj-hp-rule h2{ font-family:var(--font-display); font-weight:800; font-size:1.05rem; margin:0;
  color:var(--mj-black) }
body.dark-mode .mj-hp-rule h2{ color:#fff }
.mj-hp-rule a{ font-family:var(--font-meta); font-size:.74rem; color:var(--mj-text-muted);
  text-decoration:underline; text-underline-offset:3px; white-space:nowrap }
.mj-hp-rule a:hover{ color:var(--mj-red) }

/* the trending rail — thumbnail + kicker + title rows */
.mj-hp-rail{ display:flex; flex-direction:column }
.mj-hp-rail__row{ display:grid; grid-template-columns:76px 1fr; gap:var(--sp-4);
  padding:var(--sp-4) 0; border-top:1px solid var(--mj-rule); text-decoration:none;
  transition:background .16s ease }
.mj-hp-rail__row:first-of-type{ border-top:0 }
body.dark-mode .mj-hp-rail__row{ border-top-color:#2a2a2d }
.mj-hp-rail__row:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-hp-rail__row:hover{ background:#1b1b1e }
/* AN EMPTY IMAGE SLOT HAS TO LOOK EMPTY, NOT ABSENT. A flat --mj-ink-900
   square is within a shade of the dark homepage's own ground, so the rail's
   four thumbnails rendered as nothing at all — the rows read as a bare text
   list with a mysterious 76px indent. The big tiles already solve this with a
   diagonal hatch; the small ones now use the same one at a finer pitch, so a
   pending image is legible as a pending image at any size. */
/* A THUMBNAIL BOX WITH NO PICTURE IN IT TAKES NO SPACE (2026-09-20). The rail
   and the duo row now draw a real image when the article has one; until it does,
   the row is text, full width, with no grey rectangle standing in for a photo. */
.mj-hp-rail__th:empty, .mj-hp-duo__th:empty{ display:none }
.mj-hp-rail__row:has(.mj-hp-rail__th:empty){ grid-template-columns:1fr }
.mj-hp-duo__row:has(.mj-hp-duo__th:empty){ grid-template-columns:1fr }
.mj-hp-rail__th{ width:76px; height:60px; object-fit:cover; border-radius:2px;
  background:repeating-linear-gradient(45deg,#17171a,#17171a 7px,#212126 7px,#212126 14px) }
body:not(.dark-mode) .mj-hp-rail__th{
  background:repeating-linear-gradient(45deg,var(--mj-surface-2),var(--mj-surface-2) 7px,
    transparent 7px,transparent 14px) }
.mj-hp-rail__k{ display:block; font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.4px;
  text-transform:uppercase; font-weight:700; color:var(--mj-red); margin-bottom:4px }
.mj-hp-rail__t{ font-family:var(--font-display); font-weight:700; font-size:.92rem; line-height:1.25;
  color:var(--mj-black); margin:0 }
body.dark-mode .mj-hp-rail__t{ color:#fff }
.mj-hp-rail__row:hover .mj-hp-rail__t{ color:var(--mj-trust) }
.mj-hp-rail__m{ display:block; font-size:.72rem; color:var(--mj-gray-400); margin-top:3px }

/* the standard card grid — archives and "more from" sections */
.mj-hp-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(230px,1fr));
  gap:var(--sp-5) var(--sp-5) }
.mj-hp-card{ display:flex; flex-direction:column; text-decoration:none }
.mj-hp-card__img{ position:relative; aspect-ratio:16/10; overflow:hidden; border-radius:2px;
  background:var(--mj-ink-900); margin-bottom:12px }
.mj-hp-card__img img{ width:100%; height:100%; object-fit:cover;
  transition:transform .5s cubic-bezier(.2,.6,.2,1) }
.mj-hp-card:hover .mj-hp-card__img img{ transform:scale(1.04) }
.mj-hp-card__k{ display:block; font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.4px;
  text-transform:uppercase; font-weight:700; color:var(--mj-red); margin-bottom:5px }
.mj-hp-card__t{ font-family:var(--font-display); font-weight:700; font-size:1rem; line-height:1.25;
  color:var(--mj-black); margin:0 0 6px }
body.dark-mode .mj-hp-card__t{ color:#fff }
.mj-hp-card:hover .mj-hp-card__t{ color:var(--mj-trust) }
.mj-hp-card__d{ font-size:.86rem; line-height:1.55; color:var(--mj-text-muted); margin:0 }

/* the vertical strip — 28 verticals, the money pages */
.mj-hp-verticals{ display:grid; grid-template-columns:repeat(auto-fill,minmax(168px,1fr));
  gap:2px; background:var(--mj-rule); border:1px solid var(--mj-rule) }
body.dark-mode .mj-hp-verticals{ background:#2a2a2d; border-color:#2a2a2d }
.mj-hp-vert{ display:block; padding:var(--sp-4); background:var(--mj-surface); text-decoration:none;
  transition:background .16s ease }
body.dark-mode .mj-hp-vert{ background:#131315 }
.mj-hp-vert:hover{ background:var(--mj-ink-900) }
.mj-hp-vert__n{ font-family:var(--font-display); font-weight:800; font-size:.95rem;
  color:var(--mj-black); display:block; margin-bottom:3px }
body.dark-mode .mj-hp-vert__n{ color:#fff }
.mj-hp-vert:hover .mj-hp-vert__n{ color:#fff }
.mj-hp-vert__c{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-gray-400) }
.mj-hp-vert:hover .mj-hp-vert__c{ color:rgba(255,255,255,.6) }

/* ═══════════════════════════════════════════════════════════════════════════
   SECTION LAYOUTS — the eight shapes a vertical block can take.

   Taken from the Newsreader startups theme as LAYOUT LOGIC and rebuilt on our
   own geometry: square corners, Technor display, our kickers, our rules. The
   theme leans on photography we do not have yet, so every one of these has to
   read as finished with a placeholder in the image slot — which is why the
   text columns carry the weight and the tiles keep their ink ground.

   The point of having eight is that a vertical is not a category slot: a
   ranking wants a dominant object, an explainer wants room to argue, a news
   list wants density. One shape repeated down a homepage is the fault these
   exist to fix.
   ═════════════════════════════════════════════════════════════════════════ */

/* L6 · QUAD — one dominant tile, one tall, two stacked. Four stories, four
   different weights, no text column. For a vertical with a clear leader and
   real depth behind it. */
.mj-hp-quad{ display:grid; grid-template-columns:1.5fr 1fr 1fr; gap:2px;
  background:var(--mj-rule); margin:0 0 var(--sp-7) }
body.dark-mode .mj-hp-quad{ background:#2a2a2d }
.mj-hp-quad__col{ display:grid; grid-template-rows:1fr 1fr; gap:2px }
.mj-hp-quad .mj-hp-tile{ min-height:230px }
.mj-hp-quad .mj-hp-tile--lead{ min-height:462px }
.mj-hp-quad .mj-hp-tile--tall{ min-height:462px }

/* L2 · DUO — two equal tiles, then text rows beneath with a thumb on the
   right. Two things of the same weight is the shape of a comparison, which is
   a lot of what this desk publishes. */
.mj-hp-duo{ display:grid; grid-template-columns:1fr 1fr; gap:2px;
  background:var(--mj-rule); margin:0 0 var(--sp-5) }
body.dark-mode .mj-hp-duo{ background:#2a2a2d }
.mj-hp-duo .mj-hp-tile{ min-height:340px }
.mj-hp-duo__rows{ display:grid; grid-template-columns:1fr 1fr; gap:0 var(--sp-6);
  margin:0 0 var(--sp-7) }
.mj-hp-duo__row{ display:grid; grid-template-columns:1fr 116px; gap:var(--sp-4);
  align-items:center; padding:var(--sp-4) 0; border-bottom:1px solid var(--mj-rule);
  text-decoration:none }
.mj-hp-duo__row:hover .mj-hp-duo__t{ color:var(--mj-trust) }
.mj-hp-duo__k{ display:block; font-family:var(--font-meta); font-size:.58rem;
  letter-spacing:1.4px; text-transform:uppercase; font-weight:700;
  color:var(--mj-red); margin-bottom:5px }
.mj-hp-duo__t{ font-family:var(--font-display); font-weight:700; font-size:1.02rem;
  line-height:1.28; color:var(--mj-black); margin:0 }
body.dark-mode .mj-hp-duo__t{ color:#fff }
.mj-hp-duo__m{ display:block; font-size:.72rem; color:var(--mj-gray-400); margin-top:4px }
.mj-hp-duo__th{ width:116px; height:78px; background:var(--mj-ink-900);
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-meta); font-size:.5rem; letter-spacing:1px;
  text-transform:uppercase; color:rgba(255,255,255,.32) }

/* L4 · WIDE — one story given a full-width split, image one side and the
   argument the other, then a four-across strip under it. For the piece that
   deserves to stop the scroll without pretending to be the hero. */
.mj-hp-wide{ display:grid; grid-template-columns:1.1fr 1fr; gap:var(--sp-6);
  align-items:center; margin:0 0 var(--sp-5) }
.mj-hp-wide__img{ aspect-ratio:16/10; background:var(--mj-ink-900);
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.2px;
  text-transform:uppercase; color:rgba(255,255,255,.3) }
.mj-hp-wide__k{ display:block; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:1.5px; text-transform:uppercase; font-weight:700;
  color:var(--mj-red); margin-bottom:var(--sp-3) }
.mj-hp-wide__t{ font-family:var(--font-display); font-weight:800; font-size:2rem;
  line-height:1.14; color:var(--mj-black); margin:0 0 var(--sp-3) }
body.dark-mode .mj-hp-wide__t{ color:#fff }
.mj-hp-wide__d{ font-size:1rem; line-height:1.6; color:var(--mj-gray-400);
  margin:0 0 var(--sp-4) }
.mj-hp-wide__m{ font-family:var(--font-meta); font-size:.68rem;
  letter-spacing:.6px; color:var(--mj-gray-400) }
.mj-hp-strip{ display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:var(--sp-5); border-top:1px solid var(--mj-rule); padding-top:var(--sp-4);
  margin:0 0 var(--sp-7) }
.mj-hp-strip__i{ display:grid; grid-template-columns:72px 1fr; gap:var(--sp-3);
  text-decoration:none; align-items:start }
.mj-hp-strip__th{ width:72px; height:56px;
  background:repeating-linear-gradient(45deg,#17171a,#17171a 7px,#212126 7px,#212126 14px) }
body:not(.dark-mode) .mj-hp-strip__th{
  background:repeating-linear-gradient(45deg,var(--mj-surface-2),var(--mj-surface-2) 7px,
    transparent 7px,transparent 14px) }
.mj-hp-strip__k{ display:block; font-family:var(--font-meta); font-size:.55rem;
  letter-spacing:1.3px; text-transform:uppercase; font-weight:700;
  color:var(--mj-red); margin-bottom:4px }
.mj-hp-strip__t{ font-family:var(--font-display); font-weight:700; font-size:.88rem;
  line-height:1.26; color:var(--mj-black); margin:0 }
body.dark-mode .mj-hp-strip__t{ color:#fff }
.mj-hp-strip__i:hover .mj-hp-strip__t{ color:var(--mj-trust) }

/* L5 · INDEX — a two-column text list with rules. No images at all, and that
   is the point: it fits four times as much on a screen as a card grid and is
   the fastest thing to scan when a reader is counting what a desk has done. */
.mj-hp-index{ display:grid; grid-template-columns:1fr 1fr; gap:0 var(--sp-6);
  margin:0 0 var(--sp-7) }
.mj-hp-index__i{ display:block; padding:var(--sp-4) 0;
  border-bottom:1px solid var(--mj-rule); text-decoration:none;
  transition:padding-left .18s ease }
.mj-hp-index__i:hover{ padding-left:8px }
.mj-hp-index__k{ display:block; font-family:var(--font-meta); font-size:.55rem;
  letter-spacing:1.3px; text-transform:uppercase; font-weight:700;
  color:var(--mj-red); margin-bottom:5px }
.mj-hp-index__t{ font-family:var(--font-display); font-weight:700; font-size:.98rem;
  line-height:1.28; color:var(--mj-black); margin:0 }
body.dark-mode .mj-hp-index__t{ color:#fff }
.mj-hp-index__i:hover .mj-hp-index__t{ color:var(--mj-trust) }
.mj-hp-index__m{ display:block; font-size:.7rem; color:var(--mj-gray-400); margin-top:4px }

/* L5 · DESK — the densest section in the theme, and the one that best fits a
   desk with real depth: a feature and a text row on the left, two picture
   cards and a ruled index on the right. Eleven stories in one screen, at five
   different weights, with only three photographs asked for.

   It is built from parts that already exist — .mj-hp-card, .mj-hp-duo__rows,
   .mj-hp-index — because a section layout is an ARRANGEMENT, and inventing a
   fifth kind of card to sit inside one is how a library stops being one. */
.mj-hp-desk{ display:grid; grid-template-columns:1.05fr 1fr; gap:var(--sp-7);
  align-items:start; margin:0 0 var(--sp-7) }
.mj-hp-desk__l{ display:flex; flex-direction:column; gap:var(--sp-5) }
.mj-hp-desk__l .mj-hp-duo__rows{ grid-template-columns:1fr; margin:0;
  border-top:1px solid var(--mj-rule); padding-top:var(--sp-2) }
body.dark-mode .mj-hp-desk__l .mj-hp-duo__rows{ border-top-color:#2a2a2d }
/* THE TWO COLUMNS HAVE TO END TOGETHER (Lucky, 2026-09-20): "there ends up being
   a small gap and also an inconsistency with the fit … the best fix would be that
   you should just make the Yoco vs Capitec card have a bigger heading". The left
   column carries one feature and one row against four cards' worth on the right,
   so the feature's headline takes the weight — it is the section's lead, and it
   was set at the same size as the two supporting cards beside it. */
.mj-hp-desk__l > .mj-hp-card .mj-hp-card__t{ font-size:1.45rem; line-height:1.15;
  letter-spacing:-.01em }
.mj-hp-desk__l > .mj-hp-card .mj-hp-card__d{ font-size:.95rem; line-height:1.5 }
@media(max-width:900px){ .mj-hp-desk__l > .mj-hp-card .mj-hp-card__t{ font-size:1.2rem } }
/* AND THE LAST ROW DOES NOT HANG. With a rule above it AND below it, the closing
   row read as an unfinished table; one rule, at the top, closes the column. */
.mj-hp-desk__l .mj-hp-duo__row:last-child{ border-bottom:0; padding-bottom:0 }
.mj-hp-desk__r{ display:flex; flex-direction:column; gap:var(--sp-5) }
.mj-hp-desk__r .mj-hp-index{ margin:0 }
/* THE RIGHT COLUMN'S INDEX IS NARROWER THAN A FULL-WIDTH ONE, so its two
   columns get less room than that shape assumes. Below 1200px it reads as one. */
@media(max-width:1200px){ .mj-hp-desk__r .mj-hp-index{ grid-template-columns:1fr } }

/* PAIR — two picture cards of equal weight. Its own class rather than a
   modifier on .mj-hp-duo, because duo's tiles are OVERLAY tiles (text on the
   image) and these are CARDS (text under it). Same count, different object. */
.mj-hp-pair{ display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-5) }

/* L3 · STACK — an overlay lead beside two thumbed rows and a closing pair.
   The theme uses it for its softest section and the reason is legible: the
   lead carries its headline INSIDE the picture, so the column beside it can be
   quiet without the section looking empty. */
.mj-hp-stack{ display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-6);
  align-items:start; margin:0 0 var(--sp-7) }
.mj-hp-stack__r{ display:flex; flex-direction:column; gap:var(--sp-4) }
.mj-hp-stack__r .mj-hp-duo__rows{ grid-template-columns:1fr; margin:0 }
.mj-hp-stack__r .mj-hp-index{ margin:0; border-top:1px solid var(--mj-rule);
  padding-top:var(--sp-2) }
body.dark-mode .mj-hp-stack__r .mj-hp-index{ border-top-color:#2a2a2d }
.mj-hp-stack .mj-hp-tile{ min-height:430px }

/* DESKS OPENING NEXT — how a vertical with nothing published yet is allowed to
   appear on the homepage. A name and a plain status, on a rule, linking to a
   page that says the same thing. NOT a card: a card implies an article behind
   it, and there is none. */
.mj-hp-next{ display:grid; grid-template-columns:repeat(auto-fill,minmax(232px,1fr));
  gap:0 var(--sp-6); margin:0 0 var(--sp-6) }
.mj-hp-next__i{ display:block; padding:var(--sp-4) 0;
  border-bottom:1px solid var(--mj-rule); text-decoration:none;
  transition:padding-left .18s ease }
body.dark-mode .mj-hp-next__i{ border-bottom-color:#2a2a2d }
.mj-hp-next__i:hover{ padding-left:8px }
.mj-hp-next__n{ display:block; font-family:var(--font-display); font-weight:700;
  font-size:.98rem; color:var(--mj-black); margin:0 }
body.dark-mode .mj-hp-next__n{ color:#fff }
.mj-hp-next__i:hover .mj-hp-next__n{ color:var(--mj-trust) }
.mj-hp-next__s{ display:block; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:1.2px; text-transform:uppercase; color:var(--mj-gray-400);
  margin-top:5px }

@media(max-width:1080px){
  .mj-hp-desk, .mj-hp-stack{ grid-template-columns:1fr }
  .mj-hp-desk__r .mj-hp-index{ grid-template-columns:1fr 1fr }
  /* THREE CHILDREN DO NOT FIT TWO COLUMNS BY THEMSELVES. Dropping the quad to
     `1.4fr 1fr` left the lead and the tall in row one and the stacked column
     alone in row two — a 385×340 black hole beside it, on the widest block on
     the homepage. The lead has to be told to span, or the shape has no second
     row to be the second row OF. */
  .mj-hp-quad{ grid-template-columns:1fr 1fr }
  .mj-hp-quad .mj-hp-tile--lead{ grid-column:1 / -1; min-height:380px }
}
@media(max-width:900px){
  .mj-hp-quad, .mj-hp-duo, .mj-hp-wide, .mj-hp-index,
  .mj-hp-duo__rows, .mj-hp-pair,
  .mj-hp-desk__r .mj-hp-index{ grid-template-columns:1fr }
  .mj-hp-quad .mj-hp-tile--lead{ grid-column:auto; min-height:320px }
  .mj-hp-quad .mj-hp-tile--tall{ min-height:230px }
  .mj-hp-quad__col{ grid-template-rows:auto auto }
  .mj-hp-wide__t{ font-size:1.5rem }
}
@media(max-width:560px){
  .mj-hp-duo__row{ grid-template-columns:1fr 90px }
  .mj-hp-duo__th{ width:90px; height:64px }
}

@media(max-width:900px){
  .mj-hp-mosaic{ grid-template-columns:1fr }
  .mj-hp-tile--lead{ min-height:320px }
  .mj-hp-tile--lead .mj-hp-tile__t{ font-size:1.6rem }
}
@media(max-width:560px){
  .mj-hp-grid{ grid-template-columns:1fr }
  .mj-hp-rail__row{ grid-template-columns:64px 1fr }
  .mj-hp-rail__th{ width:64px; height:50px }
}

/* =============================================================================
   IMAGES — article-image + product-graphic  (.mj-img*)
   Built 2026-07-30 from the REAL ACF schema:
     "🖼️ Article Image – Variants (SmartCMS v4)" — article_image_variants_fc, 5 layouts:
        📸 Single · 🖼️ Side-by-Side (comparison / before-after) · 🖼️ Multi (2–4)
        · 🖼️ Overlay Caption (position: Top|Center|Bottom) · 🖼️ Fullwidth / Hero
     "🖼️ Product Graphics – Variant Entries" — product_graphic_variants_fc, 9 layouts:
        💎 Hero · 💸 Pricing screenshot · 👥 In-use · 🎨 Marketing · 🏷️ Branding
        · 📦 Unboxing · 📐 Design angle · 🧪 Feature demo (UI) · 🔮 Future feature

   THE SCHEMA'S LESSON: **every single layout carries caption + source + source_link.**
   Provenance is not optional in this system, so the design must always have somewhere to
   put it. An image with no attribution is an incomplete render — the same rule the truth
   layer applies to a price.
   ============================================================================= */
.mj-img{ margin:var(--sp-6) 0 }
.mj-img__frame{ position:relative; display:block; overflow:hidden; border-radius:2px;
  background:var(--mj-surface-2) }
body.dark-mode .mj-img__frame{ background:#17171a }
.mj-img__frame img{ display:block; width:100%; height:auto }
/* THE FILLED FRAME (2026-08-27). The frames carry an aspect-ratio and clip, so an
   image whose own ratio differs would otherwise hang out of the bottom of the box.
   Cover, because a hero crop is a composition decision made when the shot is
   chosen, not a reason to letterbox the page. */
.mj-img--filled .mj-img__frame{ background:var(--mj-surface-2) }
.mj-img__frame--filled img{ height:100%; object-fit:cover; object-position:center }
/* --shot is the wide tier with its file in it. It needs no geometry of its own —
   the tier owns that — but the L0 law is right that a class with no rule cannot
   ship: an unstyled class is a class nobody decided anything about. */
.mj-hero--shot .mj-hero__frame{ border:0 }
.mj-hero__frame--filled{ background:var(--mj-surface-2) }
.mj-hero__frame--filled img{ display:block; width:100%; height:100%;
  object-fit:cover; object-position:center }
/* caption + source: one line, source is a real link when source_link exists */
/* The parts of an image element, addressable. `__what` is the subject line of a
   pending placeholder, `__fact` the caption's own sentence, `__srclink` the
   attribution. They carry no styling their parents do not already give them —
   they exist so the words can be slotted, because a caption written into a
   template is a caption every article repeats. */
.mj-img__what{ display:block }
.mj-img__fact{ font:inherit; color:inherit }
.mj-img__srclink{ color:inherit; text-decoration:none;
  border-bottom:1px dotted currentColor }
.mj-img__srclink:hover{ border-bottom-style:solid }

.mj-img__cap{ display:flex; align-items:baseline; gap:var(--sp-3); flex-wrap:wrap;
  margin:10px 0 0; font-size:.82rem; line-height:1.5; color:var(--mj-text-muted) }
.mj-img__cap b{ color:var(--mj-ink) } body.dark-mode .mj-img__cap b{ color:#e8e8ea }
.mj-img__src{ margin-left:auto; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:.6px; text-transform:uppercase; color:var(--mj-gray-400); white-space:nowrap }
.mj-img__src a{ color:var(--mj-gray-400); text-decoration:none;
  border-bottom:1.5px dotted var(--mj-gray-300) }
.mj-img__src a:hover{ color:var(--mj-trust); border-bottom-color:var(--mj-trust);
  border-bottom-style:solid }
/* pending state — G7: a missing asset is LABELLED, never faked or stretched */
/* THE PLACEHOLDER MUST FILL THE FRAME IT IS STANDING IN FOR.
   `min-height:180px` with no height left a 16:9 frame — 383px at the reading
   measure — striped for 180px and blank for the other 203, so a PLANNED image
   read as a broken one. In the prototype as well as on the site, since both
   draw from here. It stands in for a picture, so it occupies the picture's space. */
/* A REAL PICTURE, where a card has one (2026-09-20). Every card's image box
   already sizes and crops its own <img>; this is the shared rule so a box that
   does not (an archive rail, a utility card) still crops rather than stretches.
   The pending placeholder and the picture are mutually exclusive: the renderer
   passes `image` OR `image_note`, never both. */
.mj-img__real{ display:block; width:100%; height:100%; object-fit:cover }
.mj-img__pending{ display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:6px; min-height:180px; height:100%; box-sizing:border-box;
  padding:var(--sp-5); text-align:center;
  background:repeating-linear-gradient(45deg,var(--mj-surface-2),var(--mj-surface-2) 12px,
    var(--mj-surface) 12px,var(--mj-surface) 24px);
  font-family:var(--font-meta); font-size:.62rem; letter-spacing:1.4px; text-transform:uppercase;
  color:var(--mj-gray-400) }
body.dark-mode .mj-img__pending{ background:repeating-linear-gradient(45deg,#17171a,#17171a 12px,
    #1d1d21 12px,#1d1d21 24px); color:#55555c }
.mj-img__pending b{ font-family:var(--font-display); font-size:.78rem; letter-spacing:0;
  text-transform:none; color:var(--mj-text-muted) }

/* 📸 SINGLE — the default. Sits in the reading column. */
.mj-img--single .mj-img__frame{ aspect-ratio:16/9 }

/* 🖼️ FULLWIDTH / HERO — breaks the measure */
.mj-img--full .mj-img__frame{ aspect-ratio:21/9; border-radius:0 }
.mj-img--full .mj-img__cap{ padding:0 var(--sp-2) }

/* 🖼️ SIDE-BY-SIDE — comparison / before-after. Equal by construction. */
.mj-img--sbs__grid{ display:grid; grid-template-columns:1fr 1fr; gap:2px;
  background:var(--mj-rule) }
body.dark-mode .mj-img--sbs__grid{ background:#2a2a2d }
.mj-img--sbs .mj-img__frame{ aspect-ratio:4/3; border-radius:0 }
.mj-img--sbs__lbl{ display:inline-block; margin:0 0 8px; font-family:var(--font-meta);
  font-size:.6rem; letter-spacing:1.4px; text-transform:uppercase; font-weight:700;
  color:#fff; background:var(--mj-ink-900); padding:3px 8px; border-radius:2px }
.mj-img--sbs__side{ position:relative }
.mj-img--sbs__tag{ position:absolute; top:8px; left:8px; z-index:2;
  font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.2px; text-transform:uppercase;
  font-weight:700; color:#fff; background:rgba(10,10,10,.78); padding:3px 8px; border-radius:2px }

/* 🖼️ MULTI (2–4) — a showcase strip */
.mj-img--multi__grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:var(--sp-3) }
.mj-img--multi .mj-img__frame{ aspect-ratio:1/1 }
.mj-img--multi .mj-img__cap{ font-size:.76rem; margin-top:7px }

/* 🖼️ OVERLAY CAPTION — position Top | Center | Bottom */
.mj-img--overlay .mj-img__frame{ aspect-ratio:16/9 }
.mj-img__ov{ position:absolute; left:0; right:0; z-index:2; padding:var(--sp-5);
  font-family:var(--font-display); font-weight:800; font-size:1.35rem; line-height:1.15;
  color:#fff; text-shadow:0 2px 18px rgba(0,0,0,.5) }
.mj-img__ov--top{ top:0; background:linear-gradient(to bottom,rgba(0,0,0,.62),transparent) }
.mj-img__ov--center{ top:50%; transform:translateY(-50%); text-align:center }
.mj-img__ov--bottom{ bottom:0; background:linear-gradient(to top,rgba(0,0,0,.72),transparent) }

/* PRODUCT GRAPHIC treatments — the 9 layouts share a frame but differ in TREATMENT.
   A UI screenshot needs a border (it has its own white background and would otherwise
   float); a cut-out product render needs air, not a border. */
.mj-img--shot .mj-img__frame{ border:1px solid var(--mj-rule); background:var(--mj-surface) }
body.dark-mode .mj-img--shot .mj-img__frame{ border-color:#2a2a2d; background:#131315 }
.mj-img--cutout .mj-img__frame{ background:transparent; aspect-ratio:1/1 }
.mj-img--cutout .mj-img__frame img{ object-fit:contain; padding:var(--sp-4) }
.mj-img__kind{ display:inline-flex; align-items:center; gap:6px; margin:0 0 8px;
  font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.4px; text-transform:uppercase;
  font-weight:700; color:var(--mj-gray-400) }

@media(max-width:620px){
  .mj-img--sbs__grid{ grid-template-columns:1fr }
  .mj-img__ov{ font-size:1.05rem; padding:var(--sp-4) }
  .mj-img--multi__grid{ grid-template-columns:repeat(2,1fr) }
}

/* --- Homepage section types, added 2026-07-30 --------------------------------
   Newsreader runs ~8 distinct section shapes; the first pass had 2. Variety is not
   decoration here — a homepage covering 26 verticals needs different SHAPES so a
   reader can tell "what's new" from "what we rank" from "what's worth knowing"
   without reading the section title.
   -------------------------------------------------------------------------- */

/* A · SPLIT FEATURE — one vertical gets a lead + 3 stacked links */
.mj-hp-split{ display:grid; grid-template-columns:1.15fr 1fr; gap:var(--sp-6); align-items:start }
.mj-hp-split__list{ display:flex; flex-direction:column }
.mj-hp-split__row{ display:block; padding:var(--sp-4) 0; border-top:1px solid var(--mj-rule);
  text-decoration:none; transition:padding-left .18s ease }
.mj-hp-split__row:first-child{ border-top:0 }
body.dark-mode .mj-hp-split__row{ border-top-color:#2a2a2d }
.mj-hp-split__row:hover{ padding-left:8px }
.mj-hp-split__row .t{ font-family:var(--font-display); font-weight:700; font-size:1rem;
  line-height:1.28; color:var(--mj-black); display:block }
body.dark-mode .mj-hp-split__row .t{ color:#fff }
.mj-hp-split__row:hover .t{ color:var(--mj-trust) }
.mj-hp-split__row .m{ display:block; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:1.1px; text-transform:uppercase; color:var(--mj-gray-400); margin-top:5px }

/* B · RANKED STRIP — "what we rank", a numbered horizontal run */
.mj-hp-ranked{ display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:2px;
  background:var(--mj-rule); border:1px solid var(--mj-rule) }
body.dark-mode .mj-hp-ranked{ background:#2a2a2d; border-color:#2a2a2d }
.mj-hp-ranked a{ display:block; padding:var(--sp-4); background:var(--mj-surface);
  text-decoration:none; transition:background .16s ease }
body.dark-mode .mj-hp-ranked a{ background:#131315 }
.mj-hp-ranked a:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-hp-ranked a:hover{ background:#1b1b1e }
.mj-hp-ranked .n{ display:inline-flex; align-items:center; justify-content:center; width:22px;
  height:22px; border-radius:2px; background:var(--mj-ink-900); color:#fff;
  font-family:var(--font-display); font-weight:800; font-size:.68rem; margin-bottom:9px }
body.dark-mode .mj-hp-ranked .n{ background:#fff; color:#0a0a0a }
.mj-hp-ranked .t{ display:block; font-family:var(--font-display); font-weight:700; font-size:.92rem;
  line-height:1.28; color:var(--mj-black) }
body.dark-mode .mj-hp-ranked .t{ color:#fff }
.mj-hp-ranked a:hover .t{ color:var(--mj-trust) }
.mj-hp-ranked .m{ display:block; font-size:.76rem; color:var(--mj-gray-400); margin-top:5px }

/* C · TEXT RUN — "worth knowing", explainers with no images at all.
   A section with no pictures is a deliberate change of pace, and it is honest about
   the fact that an explainer's value is the argument, not a photograph. */
.mj-hp-textrun{ display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:var(--sp-5) var(--sp-6) }
.mj-hp-textrun a{ display:block; text-decoration:none; padding-top:var(--sp-4);
  border-top:2px solid var(--mj-black) }
body.dark-mode .mj-hp-textrun a{ border-top-color:#fff }
.mj-hp-textrun .k{ display:block; font-family:var(--font-meta); font-size:.58rem;
  letter-spacing:1.4px; text-transform:uppercase; font-weight:700; color:var(--mj-red);
  margin-bottom:7px }
.mj-hp-textrun .t{ display:block; font-family:var(--font-display); font-weight:700;
  font-size:1.02rem; line-height:1.25; color:var(--mj-black); margin-bottom:6px }
body.dark-mode .mj-hp-textrun .t{ color:#fff }
.mj-hp-textrun a:hover .t{ color:var(--mj-trust) }
.mj-hp-textrun .d{ display:block; font-size:.86rem; line-height:1.55; color:var(--mj-text-muted) }

/* D · DEAL STRIP — live offers. The only homepage block that may carry a price. */
.mj-hp-deals{ display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:var(--sp-4) }
.mj-hp-deal{ display:flex; flex-direction:column; gap:6px; padding:var(--sp-4);
  border:1px solid var(--mj-rule); border-left:3px solid var(--mj-red);
  border-radius:0 var(--radius-md) var(--radius-md) 0; background:var(--mj-surface);
  text-decoration:none; transition:border-color .16s ease }
body.dark-mode .mj-hp-deal{ background:#131315; border-color:#2a2a2d; border-left-color:var(--mj-red) }
.mj-hp-deal:hover{ border-color:var(--mj-red) }
.mj-hp-deal .b{ font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.4px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400) }
.mj-hp-deal .t{ font-family:var(--font-display); font-weight:700; font-size:.95rem;
  color:var(--mj-black); line-height:1.25 }
body.dark-mode .mj-hp-deal .t{ color:#fff }
.mj-hp-deal .p{ font-family:var(--font-display); font-weight:800; font-size:1.15rem;
  color:var(--mj-black) }
body.dark-mode .mj-hp-deal .p{ color:#fff }
.mj-hp-deal .p s{ font-size:.7em; color:var(--mj-gray-400); font-weight:500; margin-left:6px }
.mj-hp-deal .e{ font-family:var(--font-meta); font-size:.58rem; letter-spacing:1px;
  text-transform:uppercase; font-weight:700; color:var(--mj-red); margin-top:auto }

@media(max-width:900px){ .mj-hp-split{ grid-template-columns:1fr } }
@media(max-width:560px){ .mj-hp-textrun{ grid-template-columns:1fr } }

/* =============================================================================
   VERTICAL ARCHIVE  (.mj-av*)  — the money page for each of 26 verticals
   Reuses .mj-hp-grid / .mj-hp-rule / .mj-hp-card from the homepage. What is new here
   is the ORGANISING IDEA: an archive is a DESK, not a list.

   A blog archive is reverse-chronological, because a blog is a diary. A review desk is
   not — a reader arriving from "card machines south africa" is somewhere in a decision,
   and the archive's job is to route them by INTENT, not by publish date. Date order
   buries the ranked roundup (the highest-intent, best-converting page) the moment
   anything newer ships.
   ============================================================================= */
/* ARCHIVE MASTHEAD — what replaced the cover banner.
   The banner was a 1600×600 black rectangle reading "Cover pending", sitting
   above the fold on every one of twenty-eight archives, and it will keep
   reading that until somebody shoots twenty-eight cover images. It occupied
   the position a section front should use to show WORK.

   So the archive now opens the way a newspaper section opens: the desk's name
   and its promise set as type, on the page's own ground, with a rule under it
   — and then immediately the stories. Newsreader's layout logic, our type and
   our geometry, exactly as asked. The dark hero remains in the library for a
   vertical that has real cover art; nothing emits it until one does. */
/* No rule under it any more, so the space below IS the separation — the h1
   carries its own bottom margin and the mosaic starts there. */
/* THE SECTION MASTHEAD (Lucky, 2026-09-20): "the name of the vertical shouldn't
   be that big … it should still feel more editorial-centric and more trustworthy",
   on ONE line, with the desk's own menu to the right of it. The title dropped from
   3.4rem to 2.4rem and lost the 19ch wrap, which is what was breaking names across
   two lines; the menu takes the room that frees. */
.mj-av-mast{ display:flex; align-items:flex-end; justify-content:space-between;
  flex-wrap:wrap; gap:var(--sp-4) var(--sp-6); padding:var(--sp-6) 0 var(--sp-3);
  margin:0 0 var(--sp-5); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-av-mast{ border-bottom-color:#2a2a2d }
.mj-av-mast__l{ min-width:0 }
.mj-av-mast__t{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(1.7rem,2.6vw,2.4rem); line-height:1.05; letter-spacing:-.02em;
  color:var(--mj-black); margin:0; white-space:nowrap }
body.dark-mode .mj-av-mast__t{ color:#fff }
.mj-av-mast__n{ margin:5px 0 0; font-family:var(--font-meta); font-size:.66rem;
  letter-spacing:.4px; color:var(--mj-text-muted) }
/* The menu reads as navigation, not as tags: one line, ruled off, the last item
   ("All 15") carrying the section's own weight. */
.mj-av-mast__nav{ display:flex; align-items:center; flex-wrap:wrap; gap:var(--sp-2) var(--sp-5);
  padding-bottom:3px }
.mj-av-mast__i{ font-family:var(--font-display); font-weight:700; font-size:.82rem;
  letter-spacing:.2px; color:var(--mj-ink); text-decoration:none; white-space:nowrap;
  border-bottom:2px solid transparent; padding-bottom:2px }
.mj-av-mast__i:hover{ color:var(--mj-red); border-bottom-color:var(--mj-red) }
body.dark-mode .mj-av-mast__i{ color:#e7e7ea }
.mj-av-mast__i--all{ color:var(--mj-trust) }
body.dark-mode .mj-av-mast__i--all{ color:#8fb4ff }
@media(max-width:820px){
  .mj-av-mast{ align-items:flex-start; flex-direction:column; gap:var(--sp-3) }
  .mj-av-mast__t{ white-space:normal }
  .mj-av-mast__nav{ gap:var(--sp-2) var(--sp-4); overflow-x:auto; max-width:100%;
    scrollbar-width:none }
  .mj-av-mast__nav::-webkit-scrollbar{ display:none }
}

.mj-av-hero{ position:relative; overflow:hidden; background:var(--mj-ink-900);
  margin:0 0 var(--sp-6); min-height:260px; display:flex; align-items:flex-end }
.mj-av-hero img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; opacity:.5 }
.mj-av-hero::after{ content:""; position:absolute; inset:0;
  background:linear-gradient(to right, rgba(10,10,10,.92) 0%, rgba(10,10,10,.55) 55%, rgba(10,10,10,.2) 100%) }
.mj-av-hero__in{ position:relative; z-index:2; padding:var(--sp-6); max-width:56ch }
.mj-av-hero__k{ display:inline-block; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:1.6px; text-transform:uppercase; font-weight:700; color:#fff;
  background:var(--mj-red); padding:3px 9px; border-radius:2px; margin-bottom:12px }
.mj-av-hero h1{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(1.8rem,4vw,2.7rem); line-height:1.06; letter-spacing:-.018em;
  color:#fff; margin:0 0 10px }
.mj-av-hero p{ margin:0; font-size:1.02rem; line-height:1.55; color:rgba(255,255,255,.8) }
.mj-av-hero__ph{ position:absolute; inset:0; z-index:1;
  background:repeating-linear-gradient(45deg,#17171a,#17171a 14px,#1d1d21 14px,#1d1d21 28px);
  display:flex; align-items:flex-start; justify-content:flex-end; padding:var(--sp-4);
  font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.4px; text-transform:uppercase;
  color:#3d3d44 }

/* the authority strip — what this desk has actually done in this vertical */
.mj-av-stats{ display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:2px;
  background:var(--mj-rule); border:1px solid var(--mj-rule); margin:0 0 var(--sp-7) }
body.dark-mode .mj-av-stats{ background:#2a2a2d; border-color:#2a2a2d }
.mj-av-stat{ background:var(--mj-surface); padding:var(--sp-4) }
body.dark-mode .mj-av-stat{ background:#131315 }
.mj-av-stat b{ display:block; font-family:var(--font-display); font-weight:800; font-size:1.5rem;
  color:var(--mj-black); line-height:1 }
body.dark-mode .mj-av-stat b{ color:#fff }
.mj-av-stat span{ display:block; font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--mj-gray-400); margin-top:6px }

/* THE INTENT ROUTER — the archive's spine. Four doors, by what the reader is doing. */
.mj-av-router{ display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:2px;
  background:var(--mj-rule); border:1px solid var(--mj-rule); margin:0 0 var(--sp-7) }
body.dark-mode .mj-av-router{ background:#2a2a2d; border-color:#2a2a2d }
.mj-av-door{ display:block; padding:var(--sp-5) var(--sp-4); background:var(--mj-surface);
  text-decoration:none; transition:background .16s ease }
body.dark-mode .mj-av-door{ background:#131315 }
.mj-av-door:hover{ background:var(--mj-ink-900) }
.mj-av-door__q{ display:block; font-family:var(--font-display); font-weight:800; font-size:1.02rem;
  color:var(--mj-black); line-height:1.25; margin-bottom:6px }
body.dark-mode .mj-av-door__q{ color:#fff }
.mj-av-door:hover .mj-av-door__q{ color:#fff }
.mj-av-door__a{ display:block; font-size:.85rem; line-height:1.5; color:var(--mj-text-muted) }
.mj-av-door:hover .mj-av-door__a{ color:rgba(255,255,255,.72) }
.mj-av-door__n{ display:inline-block; font-family:var(--font-meta); font-size:.56rem;
  letter-spacing:1.2px; text-transform:uppercase; font-weight:700; color:var(--mj-red);
  margin-top:10px }
.mj-av-door:hover .mj-av-door__n{ color:#ff8b93 }

/* the lead article — the ranked roundup gets the position date order would steal */
.mj-av-lead{ display:grid; grid-template-columns:1.4fr 1fr; gap:var(--sp-6); align-items:center;
  padding:var(--sp-5) 0; border-top:2px solid var(--mj-black);
  border-bottom:1px solid var(--mj-rule); margin:0 0 var(--sp-7) }
body.dark-mode .mj-av-lead{ border-top-color:#fff; border-bottom-color:#2a2a2d }
.mj-av-lead__t{ font-family:var(--font-display); font-weight:800; font-size:1.7rem; line-height:1.1;
  color:var(--mj-black); margin:0 0 10px }
body.dark-mode .mj-av-lead__t{ color:#fff }
.mj-av-lead__d{ font-size:.98rem; line-height:1.6; color:var(--mj-text-muted); margin:0 0 14px }
.mj-av-lead__img{ position:relative; aspect-ratio:4/3; overflow:hidden; border-radius:2px;
  background:var(--mj-ink-900) }

/* products we track — the truth-layer proof, per vertical */
.mj-av-tracked{ display:flex; flex-wrap:wrap; gap:8px; margin:0 0 var(--sp-6) }
.mj-av-chip{ display:inline-flex; align-items:baseline; gap:7px; padding:6px 11px;
  border:1px solid var(--mj-rule); border-radius:2px; background:var(--mj-surface);
  font-size:.84rem; color:var(--mj-ink); text-decoration:none; transition:border-color .16s ease }
body.dark-mode .mj-av-chip{ background:#131315; border-color:#2a2a2d; color:#e8e8ea }
.mj-av-chip:hover{ border-color:var(--mj-trust); color:var(--mj-trust) }
.mj-av-chip b{ font-family:var(--font-display); font-weight:800 }
.mj-av-chip span{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:.8px;
  color:var(--mj-gray-400) }

@media(max-width:820px){
  .mj-av-lead{ grid-template-columns:1fr }
  .mj-av-lead__img{ order:-1 }
}
@media(max-width:560px){ .mj-av-hero__in{ padding:var(--sp-5) var(--sp-4) } }

/* =============================================================================
   NEWS ARCHIVE — the Just In desk  (.mj-live*, .mj-nl-*)
   Added 2026-09-16 on Lucky's brief: "give it a dedicated archive section that is
   similar to the newsreader examples so that it can feel like it's a news vertical",
   hero taken from the CITY demo, the body built from the NEWS demo's elements.

   WHY A SECOND ARCHIVE FORM AT ALL. Every other vertical is a DESK: the reader is
   somewhere in a decision, so `.mj-av-*` routes by intent and deliberately refuses
   date order. Just In is the one vertical where date order IS the reader's question
   — "what changed?" — so it gets the one layout the others are built to avoid. That
   is the whole justification for the duplication, and it is also why none of this
   leaks into `.mj-av-*`: two archive forms, two different reader jobs.

   NO GREY FILLS. The strip and both rails are white, ruled — the same instruction
   that governs the mega menu. Structure carries the newsroom feel, not shading.
   ============================================================================= */

/* ── THE LIVE STRIP — "what has just landed", with its own navigation ──────────
   The reference runs this as a swiper under the masthead. Ours is a scroll rail:
   it swipes natively on touch, the two buttons page it on a pointer, and with no
   JS at all it is still a scrollable row of links. The red is doing its normal job
   here — red ACTS, and "live" is the most present-tense thing on the site. */
/* ONE THIN GREY RULE (Lucky, 2026-09-23): the strip's 2px black top border sat between two
   thin grey ones — the masthead's above and the strip's own below. The masthead's rule now
   does that job alone. */
.mj-live{ display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center;
  gap:var(--sp-4); margin:0 0 var(--sp-6); padding:10px 0;
  border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-live{ border-bottom-color:#2a2a2d }
.mj-live__tag{ display:inline-flex; align-items:center; gap:7px; flex:0 0 auto;
  font-family:var(--font-display); font-weight:800; font-size:.64rem; letter-spacing:1.6px;
  text-transform:uppercase; color:var(--mj-red) }
/* The same bolt as the menu's Just In item, at the strip's size. The ring pulse a
   dot used went with the dot: a box-shadow on a masked shape is masked away too,
   so the bolt flickers and glows instead, which is closer to what it is imitating. */
.mj-live__dot{ width:13px; height:13px; flex:0 0 auto; background:var(--mj-red);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M13.6 1.5 4.2 13.3c-.4.5 0 1.2.6 1.2h4.5l-1.2 7.6c-.1.7.8 1.1 1.2.5l9.5-11.8c.4-.5 0-1.2-.6-1.2h-4.5l1.2-7.6c.1-.7-.8-1.1-1.3-.5Z'/%3E%3C/svg%3E") center/contain no-repeat; mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M13.6 1.5 4.2 13.3c-.4.5 0 1.2.6 1.2h4.5l-1.2 7.6c-.1.7.8 1.1 1.2.5l9.5-11.8c.4-.5 0-1.2-.6-1.2h-4.5l1.2-7.6c.1-.7-.8-1.1-1.3-.5Z'/%3E%3C/svg%3E") center/contain no-repeat;
  animation:mj-live-pulse 1.8s ease-in-out infinite }
@keyframes mj-live-pulse{
  0%,100%{ opacity:1; filter:drop-shadow(0 0 0 rgba(236,34,39,.6)) }
  55%{ opacity:.5; filter:drop-shadow(0 0 5px rgba(236,34,39,.6)) }
}
@media(prefers-reduced-motion:reduce){ .mj-live__dot{ animation:none } }
.mj-live__rail{ display:flex; align-items:baseline; gap:var(--sp-4); min-width:0;
  overflow-x:auto; scroll-behavior:smooth; scroll-snap-type:x proximity;
  scrollbar-width:none; -ms-overflow-style:none }
.mj-live__rail::-webkit-scrollbar{ display:none }
.mj-live__i{ flex:0 0 auto; max-width:38ch; display:inline-flex; align-items:baseline; gap:8px;
  text-decoration:none; scroll-snap-align:start }
.mj-live__i + .mj-live__i{ padding-left:var(--sp-4); border-left:1px solid var(--mj-rule) }
body.dark-mode .mj-live__i + .mj-live__i{ border-left-color:#2a2a2d }
.mj-live__w{ flex:0 0 auto; font-family:var(--font-meta); font-size:.58rem; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-gray-400); font-variant-numeric:tabular-nums }
.mj-live__t{ font-family:var(--font-display); font-weight:600; font-size:.82rem; line-height:1.3;
  color:var(--mj-ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
body.dark-mode .mj-live__t{ color:#e8e8ea }
.mj-live__i:hover .mj-live__t{ color:var(--mj-red) }
.mj-live__nav{ display:flex; gap:4px; flex:0 0 auto }
.mj-live__b{ width:26px; height:26px; display:grid; place-items:center; cursor:pointer;
  font:inherit; font-size:.9rem; line-height:1; border-radius:var(--radius-md);
  border:1px solid var(--mj-rule); background:var(--mj-surface); color:var(--mj-ink);
  transition:background .14s ease, color .14s ease, border-color .14s ease }
body.dark-mode .mj-live__b{ background:#131315; border-color:#2a2a2d; color:#e8e8ea }
.mj-live__b:hover{ background:var(--mj-black); border-color:var(--mj-black); color:#fff }
/* a control that cannot do anything says so. The rail is at one of its ends. */
.mj-live__b:disabled{ opacity:.32; cursor:default }
.mj-live__b:disabled:hover{ background:var(--mj-surface); border-color:var(--mj-rule);
  color:var(--mj-ink) }
body.dark-mode .mj-live__b:disabled:hover{ background:#131315; border-color:#2a2a2d; color:#e8e8ea }

/* ── SHARED NEWS FURNITURE — kicker and byline ─────────────────────────────── */
.mj-nl-k{ display:inline-block; font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.4px;
  text-transform:uppercase; font-weight:700; color:var(--mj-red); text-decoration:none }
.mj-nl-k::before{ content:""; display:inline-block; width:5px; height:5px; border-radius:50%;
  background:currentColor; margin-right:6px; vertical-align:middle }
.mj-nl-by{ display:block; font-family:var(--font-meta); font-size:.68rem; color:var(--mj-gray-400) }
.mj-nl-by a{ color:var(--mj-ink); text-decoration:none; border-bottom:1px solid var(--mj-gray-300) }
body.dark-mode .mj-nl-by a{ color:#e8e8ea; border-bottom-color:#3a3a3f }
.mj-nl-by a:hover{ color:var(--mj-red); border-bottom-color:var(--mj-red) }

/* ── THE HERO — lifted from the CITY demo: lead beside its picture, a three-up
      under it, and the "latest" rail running the full height on the right. ──── */
.mj-nl-hero{ display:grid; grid-template-columns:minmax(0,1fr) 296px; gap:var(--sp-6);
  margin:0 0 var(--sp-7) }
.mj-nl-hero__main{ min-width:0 }
.mj-nl-lead{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);
  gap:var(--sp-5); align-items:center;
  padding:0 0 var(--sp-5); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-nl-lead{ border-bottom-color:#2a2a2d }
.mj-nl-lead__b{ min-width:0 }
.mj-nl-lead__t{ font-family:var(--font-display); font-weight:800; font-size:2.1rem; line-height:1.05;
  letter-spacing:-.02em; color:var(--mj-black); margin:9px 0 11px }
body.dark-mode .mj-nl-lead__t{ color:#fff }
/* THE HEADLINE'S LINK CARRIES A CLASS, and that is not decoration: the
   extractor matches on class tokens only, so a bare `a` inside the heading
   could never become a slot and every built card would ship the prototype's
   own headline and href. A template layer that cannot address an element is a
   template layer that bakes it. */
.mj-nl-lead__a{ color:inherit; text-decoration:none }
.mj-nl-lead__a:hover{ text-decoration:underline; text-underline-offset:3px;
  text-decoration-thickness:2px; text-decoration-color:var(--mj-red) }
.mj-nl-lead__d{ font-size:1rem; line-height:1.6; color:var(--mj-text-muted); margin:0 0 12px }
.mj-nl-lead__img{ position:relative; display:block; aspect-ratio:4/3; overflow:hidden;
  border-radius:var(--radius-md) }
.mj-nl-lead__img img{ width:100%; height:100%; object-fit:cover; display:block;
  transition:transform .4s ease }
.mj-nl-lead__img:hover img{ transform:scale(1.03) }

.mj-nl-subs{ display:grid; grid-template-columns:repeat(auto-fit,minmax(170px,1fr));
  gap:var(--sp-5); padding:var(--sp-5) 0 0 }
.mj-nl-sub{ min-width:0 }
.mj-nl-sub__img{ position:relative; display:block; aspect-ratio:16/10; overflow:hidden;
  border-radius:var(--radius-md); margin:0 0 11px }
.mj-nl-sub__img img{ width:100%; height:100%; object-fit:cover; display:block }
.mj-nl-sub__t{ font-family:var(--font-display); font-weight:800; font-size:1rem; line-height:1.24;
  color:var(--mj-black); margin:7px 0 7px }
body.dark-mode .mj-nl-sub__t{ color:#fff }
.mj-nl-sub__a{ color:inherit; text-decoration:none }
.mj-nl-sub__a:hover{ color:var(--mj-red) }

/* the right-hand rail. White and ruled — the newsroom look comes from the rule
   and the heading, never from a grey panel. */
.mj-nl-rail{ min-width:0; align-self:start; border-left:1px solid var(--mj-rule);
  padding-left:var(--sp-5) }
body.dark-mode .mj-nl-rail{ border-left-color:#2a2a2d }
.mj-nl-rail__h{ font-family:var(--font-display); font-weight:800; font-size:.72rem;
  letter-spacing:1.6px; text-transform:uppercase; color:var(--mj-black);
  margin:0 0 var(--sp-3); padding:0 0 8px; border-bottom:2px solid var(--mj-black) }
body.dark-mode .mj-nl-rail__h{ color:#fff; border-bottom-color:#fff }
/* a second heading in the same rail spaces itself off the list above it —
   a sibling rule, so the build does not have to hand a modifier class down. */
.mj-nl-side .mj-desk + .mj-nl-rail__h{ margin-top:var(--sp-6) }
.mj-nl-rail__i{ display:grid; grid-template-columns:72px minmax(0,1fr); gap:12px;
  padding:var(--sp-4) 0; border-bottom:1px solid var(--mj-rule); text-decoration:none }
body.dark-mode .mj-nl-rail__i{ border-bottom-color:#2a2a2d }
.mj-nl-rail__i:last-child{ border-bottom:0; padding-bottom:0 }
.mj-nl-rail__i--lead{ grid-template-columns:minmax(0,1fr) }
.mj-nl-rail__img{ position:relative; display:block; aspect-ratio:1/1; overflow:hidden;
  border-radius:var(--radius-md) }
.mj-nl-rail__i--lead .mj-nl-rail__img{ aspect-ratio:16/10 }
.mj-nl-rail__img img{ width:100%; height:100%; object-fit:cover; display:block }
.mj-nl-rail__b{ min-width:0 }
.mj-nl-rail__t{ display:block; font-family:var(--font-display); font-weight:700; font-size:.86rem;
  line-height:1.3; color:var(--mj-black); margin:5px 0 5px }
body.dark-mode .mj-nl-rail__t{ color:#f0f0f2 }
.mj-nl-rail__i--lead .mj-nl-rail__t{ font-size:1.05rem; line-height:1.24 }
.mj-nl-rail__i:hover .mj-nl-rail__t{ color:var(--mj-red) }

/* ── THE BOARD — the NEWS demo's stream, with the numbered strip beside it.
      `.mj-desk` is reused verbatim for "most read": it already collapses to one
      column at this width, and a second numbered list would have been the same
      element with a different name. ──────────────────────────────────────────── */
.mj-nl-board{ display:grid; grid-template-columns:minmax(0,1fr) 296px; gap:var(--sp-6);
  margin:0 0 var(--sp-7) }
.mj-nl-stream{ min-width:0 }
.mj-nl-row{ display:grid; grid-template-columns:200px minmax(0,1fr); gap:var(--sp-5);
  padding:var(--sp-5) 0; border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-nl-row{ border-top-color:#2a2a2d }
.mj-nl-row:first-child{ border-top:0; padding-top:0 }
.mj-nl-row__img{ position:relative; display:block; aspect-ratio:4/3; overflow:hidden;
  border-radius:var(--radius-md) }
.mj-nl-row__img img{ width:100%; height:100%; object-fit:cover; display:block }
.mj-nl-row__b{ min-width:0 }
.mj-nl-row__t{ font-family:var(--font-display); font-weight:800; font-size:1.18rem; line-height:1.22;
  color:var(--mj-black); margin:7px 0 7px }
body.dark-mode .mj-nl-row__t{ color:#fff }
.mj-nl-row__a{ color:inherit; text-decoration:none }
.mj-nl-row__a:hover{ color:var(--mj-red) }
.mj-nl-row__d{ font-size:.9rem; line-height:1.55; color:var(--mj-text-muted); margin:0 0 8px }
.mj-nl-side{ min-width:0; align-self:start; position:sticky; top:84px }

/* ── A STORY WITHOUT A PICTURE IS STILL A STORY ────────────────────────────
   Every image cell is conditional in the template layer: the desk publishes
   long before its photography does, and eight grey "image pending" frames down
   one page is a worse page than no pictures at all. So each of these layouts
   drops its picture column when the picture is not there, rather than leaving
   the headline squeezed into the 200px slot the photograph was going to use. */
.mj-nl-lead:not(:has(.mj-nl-lead__img)),
.mj-nl-row:not(:has(.mj-nl-row__img)),
.mj-nl-rail__i:not(:has(.mj-nl-rail__img)){ grid-template-columns:minmax(0,1fr) }
.mj-nl-side + .mj-nl-side{ margin-top:var(--sp-6) }

@media(max-width:1024px){
  .mj-nl-hero, .mj-nl-board{ grid-template-columns:minmax(0,1fr) }
  .mj-nl-rail{ border-left:0; padding-left:0; border-top:1px solid var(--mj-rule);
    padding-top:var(--sp-5) }
  body.dark-mode .mj-nl-rail{ border-top-color:#2a2a2d }
  .mj-nl-rail__i--lead{ grid-template-columns:72px minmax(0,1fr) }
  .mj-nl-rail__i--lead .mj-nl-rail__img{ aspect-ratio:1/1 }
  .mj-nl-rail__i--lead .mj-nl-rail__t{ font-size:.86rem; line-height:1.3 }
  .mj-nl-side{ position:static }
}
@media(max-width:820px){
  .mj-nl-lead{ grid-template-columns:minmax(0,1fr) }
  /* THE PICTURE MUST NOT EAT THE FOLD. At 4:3 and full column width the lead
     photograph ran 564px tall on a 900px screen and pushed the headline — the
     thing the reader came for — below it. Stacked, it takes a letterbox. */
  .mj-nl-lead__img{ order:-1; aspect-ratio:16/9 }
  .mj-nl-lead__t{ font-size:1.75rem }
}
@media(max-width:560px){
  .mj-live{ grid-template-columns:auto minmax(0,1fr) }
  .mj-live__nav{ display:none }
  .mj-nl-lead__t{ font-size:1.5rem }
  .mj-nl-row{ grid-template-columns:minmax(0,1fr) }
  .mj-nl-row__img{ order:-1 }
  .mj-nl-row__t{ font-size:1.08rem }
}

/* =============================================================================
   ARTICLE SINGLE — 🛠 HOW-TO GUIDE  (.mj-ht*)
   Purpose: a PROCEDURE. The reader is not deciding, they are doing — often with the
   thing in front of them and one hand free. So the template optimises for POSITION
   ("where am I in this?") rather than for argument.
   Spine: smart-visual-block block_type = 🪜 Step-by-Step, plus the two units below.
   ============================================================================= */
/* before you start — the prerequisites bar. A procedure that fails at step 4 for want
   of something nobody mentioned is worse than no guide at all. */
.mj-ht-pre{ margin:var(--sp-6) 0; border:1px solid var(--mj-rule);
  border-left:4px solid var(--mj-trust); border-radius:0 var(--radius-md) var(--radius-md) 0;
  background:var(--mj-surface) }
body.dark-mode .mj-ht-pre{ background:#131315; border-color:#2a2a2d; border-left-color:var(--mj-trust) }
.mj-ht-pre__h{ display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-3) var(--sp-5);
  font-family:var(--font-meta); font-size:.66rem; letter-spacing:1.6px; text-transform:uppercase;
  font-weight:700; color:var(--mj-trust); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-ht-pre__h{ border-bottom-color:#2a2a2d }
.mj-ht-pre__grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(170px,1fr)) }
.mj-ht-pre__i{ padding:var(--sp-4) var(--sp-5); border-right:1px solid var(--mj-rule) }
.mj-ht-pre__i:last-child{ border-right:0 }
body.dark-mode .mj-ht-pre__i{ border-right-color:#2a2a2d }
.mj-ht-pre__i b{ display:block; font-family:var(--font-display); font-weight:800; font-size:.95rem;
  color:var(--mj-black); margin-bottom:4px }
body.dark-mode .mj-ht-pre__i b{ color:#fff }
.mj-ht-pre__i span{ font-size:.86rem; line-height:1.5; color:var(--mj-text-muted) }
/* the procedure meta — time, difficulty, cost. Answered before the first step. */
.mj-ht-meta{ display:flex; flex-wrap:wrap; gap:var(--sp-5); padding:var(--sp-4) 0;
  margin:var(--sp-5) 0; border-top:2px solid var(--mj-black); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-ht-meta{ border-top-color:#fff; border-bottom-color:#2a2a2d }
.mj-ht-meta__i b{ display:block; font-family:var(--font-display); font-weight:800; font-size:1.15rem;
  color:var(--mj-black); line-height:1 }
body.dark-mode .mj-ht-meta__i b{ color:#fff }
.mj-ht-meta__i span{ display:block; font-family:var(--font-meta); font-size:.58rem;
  letter-spacing:1.2px; text-transform:uppercase; color:var(--mj-gray-400); margin-top:5px }
/* the steps — numbered, generously spaced, each one a complete action */
.mj-ht-step{ display:grid; grid-template-columns:auto 1fr; gap:var(--sp-4);
  padding:var(--sp-5) 0; border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-ht-step{ border-top-color:#2a2a2d }
.mj-ht-step:first-of-type{ border-top:0 }
.mj-ht-step__h{ font-family:var(--font-display); font-weight:800; font-size:1.2rem; line-height:1.2;
  color:var(--mj-black); margin:0 0 8px }
body.dark-mode .mj-ht-step__h{ color:#fff }
.mj-ht-step p{ margin:0 0 var(--sp-3); font-size:1rem; line-height:1.7; color:var(--mj-ink) }
body.dark-mode .mj-ht-step p{ color:#e8e8ea }
/* the check — what "done" looks like. Every step ends with a verifiable state. */
.mj-ht-step .mj-ht-check,.mj-ht-check{ display:flex; align-items:baseline; gap:9px; margin:var(--sp-3) 0 0;
  padding:10px var(--sp-4); background:var(--mj-growth-subtle); border-radius:2px;
  font-size:.9rem; line-height:1.5; color:#08694c }
body.dark-mode .mj-ht-check{ background:rgba(16,185,129,.10); color:#34d8a6 }
.mj-ht-check::before{ content:"✓"; font-weight:800; flex:0 0 auto }
/* the trap — where people get stuck. Earned by testing, not invented. */
.mj-ht-step .mj-ht-trap,.mj-ht-trap{ display:flex; align-items:baseline; gap:9px; margin:var(--sp-3) 0 0;
  padding:10px var(--sp-4); background:var(--mj-alert-subtle); border-radius:2px;
  font-size:.9rem; line-height:1.5; color:var(--mj-alert) }
body.dark-mode .mj-ht-trap{ background:rgba(193,18,31,.13); color:#ff8b93 }
.mj-ht-trap::before{ content:"!"; font-family:var(--font-display); font-weight:800; flex:0 0 auto }
@media(max-width:560px){
  .mj-ht-step{ grid-template-columns:1fr; gap:var(--sp-3) }
  .mj-ht-pre__i{ border-right:0; border-bottom:1px solid var(--mj-rule) }
}

/* =============================================================================
   ARTICLE SINGLE — ❓ FAQ  (.mj-fq*)
   Purpose: a SET OF ANSWERS. Shortest budget in the system (700/1,000/1,400) and the
   last-chance conversion surface, so its internal links are bold blue (W5).
   Built from "❓ FAQ – Core Content & Writing": faq_short_answer is SEPARATE from
   faq_answer_variants_fc (📝 Standard · 📋 Bullets · 🧩 Multimedia · 🎥 Video).
   THE SHORT ANSWER IS THE SNIPPET; the variant is the depth. Same architecture as
   glossary-term: one line where the reader is, the full thing one level down.
   ============================================================================= */
.mj-fq-jump{ display:flex; flex-wrap:wrap; gap:8px; margin:var(--sp-5) 0 var(--sp-7) }
/* OBSERVATION 20.2 — the jump nav is a set of CONTROLS, so it takes the
   display face, not the body face. Lucky asked which of the two is correct
   "since we have reasons why we use specific fonts": on this site
   `--font-display` marks a thing you act on (buttons, CTAs, nav) and
   `--font-body` marks a thing you read. These are jump targets — the reader
   presses them to move — so they belong with the buttons. Kept at the same size
   and weight as the surrounding meta so the change is a change of voice, not a
   change of loudness. */
.mj-fq-jump a{ display:inline-block; padding:7px 12px;
  font-family:var(--font-display); font-weight:600; font-size:.78rem;
  letter-spacing:.4px;
  border:1px solid var(--mj-rule); border-radius:2px; background:var(--mj-surface);
  color:var(--mj-ink); text-decoration:none; transition:border-color .14s ease, color .14s ease }
body.dark-mode .mj-fq-jump a{ background:#131315; border-color:#2a2a2d; color:#e8e8ea }
.mj-fq-jump a:hover{ border-color:var(--mj-trust); color:var(--mj-trust) }
.mj-fq-group{ margin:var(--sp-7) 0 }
/* line-height declared: without it this borrowed the global heading rule, which
   the ownership probe reports as a foreign selector winning. Partial typography
   is a dependency on whatever else is in the room. */
.mj-fq-group__h{ font-family:var(--font-display); font-weight:800; font-size:1.25rem;
  line-height:1.2; color:var(--mj-black); margin:0 0 var(--sp-4); padding-top:10px;
  border-top:2px solid var(--mj-black); scroll-margin-top:80px }
body.dark-mode .mj-fq-group__h{ color:#fff; border-top-color:#fff }
.mj-fq-q{ padding:var(--sp-5) 0; border-top:1px solid var(--mj-rule) }
.mj-fq-q:first-of-type{ border-top:0 }
body.dark-mode .mj-fq-q{ border-top-color:#2a2a2d }
.mj-fq-q__h{ font-family:var(--font-display); font-weight:800; font-size:1.08rem; line-height:1.3;
  color:var(--mj-black); margin:0 0 10px }
body.dark-mode .mj-fq-q__h{ color:#fff }
/* faq_short_answer — the snippet. Set apart so a scanner gets the answer without reading on. */
.mj-fq-short{ display:block; font-size:1rem; line-height:1.6; color:var(--mj-black);
  font-weight:700; padding-left:var(--sp-4); border-left:3px solid var(--mj-growth);
  margin:0 0 var(--sp-3) }
body.dark-mode .mj-fq-short{ color:#fff }
.mj-fq-q p{ margin:0 0 var(--sp-3); font-size:.98rem; line-height:1.7; color:var(--mj-text-muted) }
.mj-fq-q p:last-child{ margin-bottom:0 }
/* W5 — FAQ internal links are BOLD blue. Last-chance surface. */
.mj-fq-q .mj-link--faq{ color:var(--mj-trust); font-weight:800 }

/* =============================================================================
   ARTICLE SINGLE — 💸 DEALS & COUPONS  (.mj-dl*)
   Built from "💸 SmartCMS Coupons – Core & Writing (SmartCMS v4)":
     coupon_title · coupon_type (🔢 Code | 🔗 Link) · coupon_code · coupon_link
     · button_label · discount_label · coupon_note
     · expiry_date (date_picker) · urgency_toggle (true_false)

   ⚑ THE URGENCY_TOGGLE IS THE FLASHPOINT v3 PART 5 WAS WRITTEN FOR.
   A boolean that switches urgency on regardless of the date is precisely the fabrication
   the ruling rejects. So the binding is: **urgency_toggle is an EDITORIAL PERMISSION,
   never a source of truth.**
        toggle ON  + real expiry_date within 7 days  → urgency renders
        toggle ON  + expiry_date absent or far       → NOTHING renders
        toggle OFF                                    → nothing, regardless of date
   The date decides; the toggle only allows. An editor can suppress urgency on a real
   deadline, but cannot manufacture one.

   SECOND LAW: an EXPIRED deal does not vanish — it renders as expired, greyed, with its
   end date. Deleting it hides that we were tracking; showing it proves we still are, and
   a reader who sees an honest "ended 30 Jun" trusts the live ones more.
   ============================================================================= */
.mj-dl-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
  gap:var(--sp-5); margin:var(--sp-6) 0 }
.mj-dl{ display:flex; flex-direction:column; gap:10px; padding:var(--sp-5);
  border:1px solid var(--mj-rule); border-top:3px solid var(--mj-red);
  border-radius:0 0 var(--radius-md) var(--radius-md); background:var(--mj-surface);
  transition:border-color .16s ease, box-shadow .16s ease }
body.dark-mode .mj-dl{ background:#131315; border-color:#2a2a2d; border-top-color:var(--mj-red) }
.mj-dl:hover{ box-shadow:0 6px 22px rgba(0,0,0,.07) }
.mj-dl__brand{ display:flex; align-items:center; gap:9px; font-family:var(--font-meta);
  font-size:.6rem; letter-spacing:1.4px; text-transform:uppercase; font-weight:700;
  color:var(--mj-gray-400) }
.mj-dl__t{ font-family:var(--font-display); font-weight:800; font-size:1.08rem; line-height:1.25;
  color:var(--mj-black); margin:0 }
body.dark-mode .mj-dl__t{ color:#fff }
/* discount_label — the number the reader came for */
.mj-dl__disc{ font-family:var(--font-display); font-weight:800; font-size:1.9rem; line-height:1;
  color:var(--mj-black) }
body.dark-mode .mj-dl__disc{ color:#fff }
.mj-dl__disc s{ font-size:.5em; color:var(--mj-gray-400); font-weight:500; margin-left:8px }
.mj-dl__note{ font-size:.86rem; line-height:1.55; color:var(--mj-text-muted); margin:0 }
/* coupon_type = 🔢 Code — a copyable code block, not a button */
.mj-dl__code{ display:flex; align-items:stretch; border:1px dashed var(--mj-gray-300);
  border-radius:2px; overflow:hidden; background:var(--mj-surface-2) }
body.dark-mode .mj-dl__code{ background:#1a1a1d; border-color:#3a3a3d }
.mj-dl__code code{ flex:1 1 auto; padding:11px var(--sp-4); font-family:var(--font-display);
  font-weight:800; font-size:1rem; letter-spacing:1.5px; color:var(--mj-black);
  display:flex; align-items:center }
body.dark-mode .mj-dl__code code{ color:#fff }
.mj-dl__copy{ flex:0 0 auto; padding:0 var(--sp-4); border:0; border-left:1px dashed var(--mj-gray-300);
  background:var(--mj-ink-900); color:#fff; font-family:var(--font-display); font-weight:800;
  font-size:.7rem; letter-spacing:1px; text-transform:uppercase; cursor:pointer;
  transition:background .16s ease }
.mj-dl__copy:hover{ background:var(--mj-red) }
.mj-dl__copy.is-done{ background:var(--mj-growth) }
/* the status line — verified date always, expiry only when real */
.mj-dl__meta{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap; margin-top:auto;
  padding-top:10px; border-top:1px solid var(--mj-rule);
  font-family:var(--font-meta); font-size:.58rem; letter-spacing:1px; text-transform:uppercase;
  color:var(--mj-gray-400) }
body.dark-mode .mj-dl__meta{ border-top-color:#2a2a2d }
/* urgency — renders ONLY from a real, near expiry_date (v3 Part 5) */
.mj-dl__urgent{ margin-left:auto; font-weight:700; color:var(--mj-alert);
  background:var(--mj-alert-subtle); padding:2px 7px; border-radius:2px }
body.dark-mode .mj-dl__urgent{ background:rgba(193,18,31,.16); color:#ff8b93 }
.mj-dl__ends{ margin-left:auto; font-weight:700; color:var(--mj-text-muted) }
/* EXPIRED — shown, not deleted */
.mj-dl--expired{ border-top-color:var(--mj-gray-300); opacity:.72 }
.mj-dl--expired .mj-dl__disc,.mj-dl--expired .mj-dl__t{ color:var(--mj-gray-400) }
body.dark-mode .mj-dl--expired .mj-dl__disc,body.dark-mode .mj-dl--expired .mj-dl__t{ color:#6a6a72 }
.mj-dl--expired .mj-dl__code{ opacity:.5 }
.mj-dl__expired-tag{ margin-left:auto; font-weight:700; color:var(--mj-gray-400) }
/* the verification strip — why a deals page can be trusted at all */
.mj-dl-verify{ display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:2px;
  background:var(--mj-rule); border:1px solid var(--mj-rule); margin:var(--sp-6) 0 }
body.dark-mode .mj-dl-verify{ background:#2a2a2d; border-color:#2a2a2d }
.mj-dl-verify__i{ background:var(--mj-surface); padding:var(--sp-4) }
body.dark-mode .mj-dl-verify__i{ background:#131315 }
.mj-dl-verify__i b{ display:block; font-family:var(--font-display); font-weight:800; font-size:.95rem;
  color:var(--mj-black); margin-bottom:4px }
body.dark-mode .mj-dl-verify__i b{ color:#fff }
.mj-dl-verify__i span{ font-size:.86rem; line-height:1.5; color:var(--mj-text-muted) }
@media(max-width:560px){ .mj-dl-grid{ grid-template-columns:1fr } }

/* =============================================================================
   ENTITY ARCHIVES — brand-profile + product  (.mj-ent*)
   Built from the real schemas:
     brand-profile → brand_brand_colors{primary,secondary,faint} · brand_short/full_description
                     · brand_ideal_customers · brand_trust_notes · brand_trust_indicators (checkbox)
                     · brand_brand_website · affiliate_program (true_false)
                     · affiliate_program_details{commission_rate,program_terms,program_contact}
     product       → product_type (Physical|Digital|Service|Bundle|API|Subscription)
                     · product_license_type · product_sku · product_launch_date
                     · 16 RELATIONSHIP fields: linked_articles/_brand/_vendor/_prices/_reviews/
                       _comparison_tables/_glossary_terms/_ctas/_boxes/_kits/…

   THE SCHEMA'S LESSON: `product` carries SIXTEEN relationship fields. A product is not a page,
   it is a NODE in an entity graph. So the product archive's job is not to describe the product
   again — the review already does that — but to show EVERYTHING WE KNOW that touches it, and
   let the reader enter from whichever direction they arrived.

   ⚑ `affiliate_program` (true_false) EXISTS on brand-profile. Earlier mappings recorded it as a
   gap; that was wrong. It is the sponsor flag, and it must be rendered honestly: a brand we earn
   from says so on its own hub page.
   ============================================================================= */
.mj-ent-hero{ display:grid; grid-template-columns:auto 1fr auto; gap:var(--sp-5); align-items:center;
  padding:var(--sp-6) 0; margin:0 0 var(--sp-6);
  border-top:3px solid var(--brand-accent,var(--mj-ink-900)); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-ent-hero{ border-bottom-color:#2a2a2d }
.mj-ent-hero__logo{ width:76px; height:76px; border-radius:2px; object-fit:contain;
  background:var(--mj-surface-2); padding:10px; flex:0 0 auto }
body.dark-mode .mj-ent-hero__logo{ background:#1a1a1d }
.mj-ent-hero__k{ display:flex; align-items:center; gap:9px; flex-wrap:wrap; margin:0 0 6px;
  font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.5px; text-transform:uppercase;
  font-weight:700; color:var(--mj-gray-400) }
.mj-ent-hero h1{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(1.7rem,3.6vw,2.4rem); line-height:1.06; letter-spacing:-.015em;
  color:var(--mj-black); margin:0 0 8px }
body.dark-mode .mj-ent-hero h1{ color:#fff }
.mj-ent-hero__d{ font-size:1rem; line-height:1.6; color:var(--mj-text-muted); margin:0; max-width:56ch }
.mj-ent-hero__act{ text-align:right; flex:0 0 auto }
.mj-ent-hero__price{ display:block; font-family:var(--font-display); font-weight:800; font-size:1.5rem;
  color:var(--mj-black); line-height:1; margin-bottom:4px }
body.dark-mode .mj-ent-hero__price{ color:#fff }
.mj-ent-hero__note{ display:block; font-size:.74rem; color:var(--mj-gray-400); margin-bottom:10px }

/* the sponsor line — affiliate_program = true is DISCLOSED, not hidden */
.mj-ent-sponsor{ display:flex; align-items:baseline; gap:9px; margin:var(--sp-3) 0 0;
  padding:9px var(--sp-4); border-left:3px solid var(--mj-trust); background:var(--mj-trust-subtle);
  font-size:.86rem; line-height:1.55; color:var(--mj-ink) }
body.dark-mode .mj-ent-sponsor{ background:rgba(37,99,235,.10); color:#e8e8ea }
.mj-ent-sponsor b{ color:var(--mj-black) } body.dark-mode .mj-ent-sponsor b{ color:#fff }
.mj-ent-sponsor--none{ border-left-color:var(--mj-gray-300); background:var(--mj-surface-2) }
body.dark-mode .mj-ent-sponsor--none{ background:#1a1a1d }

/* brand_trust_indicators — the checkbox set, as marks not badges */
.mj-ent-trust{ display:flex; flex-wrap:wrap; gap:var(--sp-3) var(--sp-5); margin:var(--sp-4) 0;
  padding:var(--sp-4) 0; border-top:1px solid var(--mj-rule); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-ent-trust{ border-color:#2a2a2d }
.mj-ent-trust span{ display:inline-flex; align-items:baseline; gap:8px; font-size:.88rem;
  color:var(--mj-text-muted) }
.mj-ent-trust span::before{ content:"✓"; color:#08694c; font-weight:800; flex:0 0 auto }
body.dark-mode .mj-ent-trust span::before{ color:#34d8a6 }

/* the entity graph — what links to this node */
.mj-ent-graph{ display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:2px;
  background:var(--mj-rule); border:1px solid var(--mj-rule); margin:var(--sp-6) 0 }
body.dark-mode .mj-ent-graph{ background:#2a2a2d; border-color:#2a2a2d }
.mj-ent-graph a{ display:block; padding:var(--sp-4); background:var(--mj-surface);
  text-decoration:none; transition:background .16s ease }
body.dark-mode .mj-ent-graph a{ background:#131315 }
.mj-ent-graph a:hover{ background:var(--mj-ink-900) }
.mj-ent-graph b{ display:block; font-family:var(--font-display); font-weight:800; font-size:1.4rem;
  color:var(--mj-black); line-height:1 }
body.dark-mode .mj-ent-graph b{ color:#fff }
.mj-ent-graph a:hover b,.mj-ent-graph a:hover span{ color:#fff }
.mj-ent-graph span{ display:block; font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--mj-gray-400); margin-top:6px }

/* price history — dated rows, the truth layer over time */
.mj-ent-price{ width:100%; border-collapse:collapse; margin:var(--sp-5) 0; font-size:.92rem }
.mj-ent-price th{ text-align:left; padding:var(--sp-3); background:var(--mj-ink-900); color:#fff;
  font-family:var(--font-meta); font-size:.62rem; letter-spacing:1.3px; text-transform:uppercase }
.mj-ent-price td{ padding:var(--sp-3); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-ent-price td{ border-bottom-color:#2a2a2d }
.mj-ent-price tbody tr:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-ent-price tbody tr:hover{ background:#1b1b1e }
.mj-ent-price td:first-child{ font-weight:700; color:var(--mj-black); white-space:nowrap }
body.dark-mode .mj-ent-price td:first-child{ color:#fff }
.mj-ent-price .up{ color:var(--mj-alert); font-weight:700 }
.mj-ent-price .down{ color:#08694c; font-weight:700 }
body.dark-mode .mj-ent-price .down{ color:#34d8a6 }

@media(max-width:760px){
  .mj-ent-hero{ grid-template-columns:auto 1fr; gap:var(--sp-4) }
  .mj-ent-hero__act{ grid-column:1/-1; text-align:left; padding-top:var(--sp-4);
    border-top:1px solid var(--mj-rule) }
}

/* =============================================================================
   UTILITY ARCHIVES — glossary · author · subcategory · search  (.mj-ua*)
   These four share one structural problem: a filtered list of things, where the
   FILTER is the design. They reuse .mj-hp-grid / -rule / -card / -textrun and add
   only the affordances a list needs — an A–Z index, a result count, a query echo.
   ============================================================================= */
.mj-ua-head{ padding:var(--sp-6) 0 var(--sp-5); border-bottom:1px solid var(--mj-rule);
  margin-bottom:var(--sp-6) }
body.dark-mode .mj-ua-head{ border-bottom-color:#2a2a2d }
.mj-ua-head__k{ display:inline-block; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:1.5px; text-transform:uppercase; font-weight:700; color:#fff;
  background:var(--mj-ink-900); padding:3px 9px; border-radius:2px; margin-bottom:12px }
.mj-ua-head h1{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(1.7rem,3.6vw,2.4rem); line-height:1.06; letter-spacing:-.015em;
  color:var(--mj-black); margin:0 0 8px }
body.dark-mode .mj-ua-head h1{ color:#fff }
.mj-ua-head p{ margin:0; font-size:1rem; line-height:1.6; color:var(--mj-text-muted); max-width:56ch }
.mj-ua-count{ display:block; font-family:var(--font-meta); font-size:.64rem; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--mj-gray-400); margin-top:10px; font-weight:700 }

/* A–Z index — the glossary's spine */
.mj-ua-az{ display:flex; flex-wrap:wrap; gap:4px; margin:0 0 var(--sp-6) }
.mj-ua-az a{ display:inline-flex; align-items:center; justify-content:center; min-width:30px;
  height:30px; padding:0 7px; border:1px solid var(--mj-rule); border-radius:2px;
  font-family:var(--font-display); font-weight:800; font-size:.78rem; color:var(--mj-black);
  text-decoration:none; transition:background .14s ease, color .14s ease, border-color .14s ease }
body.dark-mode .mj-ua-az a{ border-color:#2a2a2d; color:#fff }
.mj-ua-az a:hover{ background:var(--mj-ink-900); color:#fff; border-color:var(--mj-ink-900) }
.mj-ua-az a.is-empty{ color:var(--mj-gray-300); pointer-events:none; border-style:dashed }

/* the glossary list — term + short definition, two columns of reading */
.mj-ua-terms{ display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:0 var(--sp-6) }
.mj-ua-term{ padding:var(--sp-4) 0; border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-ua-term{ border-top-color:#2a2a2d }
.mj-ua-term__t{ font-family:var(--font-display); font-weight:800; font-size:1rem; margin:0 0 5px }
.mj-ua-term__t a{ color:var(--mj-black); text-decoration:none;
  border-bottom:1.5px dotted var(--mj-trust) }
body.dark-mode .mj-ua-term__t a{ color:#fff }
.mj-ua-term__t a:hover{ color:var(--mj-trust); border-bottom-style:solid }
.mj-ua-term__d{ margin:0; font-size:.9rem; line-height:1.6; color:var(--mj-text-muted) }

/* search — the query echo and the result rows */
.mj-ua-q{ display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-4);
  border:1px solid var(--mj-rule); border-radius:var(--radius-md); background:var(--mj-surface-2);
  margin:0 0 var(--sp-5) }
body.dark-mode .mj-ua-q{ background:#1a1a1d; border-color:#2a2a2d }
.mj-ua-q input{ flex:1 1 auto; border:0; background:transparent; font-family:var(--font-body);
  font-size:1.02rem; color:var(--mj-ink); outline:none }
body.dark-mode .mj-ua-q input{ color:#e8e8ea }
.mj-ua-result{ display:block; padding:var(--sp-4) 0; border-top:1px solid var(--mj-rule);
  text-decoration:none; transition:padding-left .18s ease }
body.dark-mode .mj-ua-result{ border-top-color:#2a2a2d }
.mj-ua-result:hover{ padding-left:8px }
.mj-ua-result__k{ display:inline-block; font-family:var(--font-meta); font-size:.56rem;
  letter-spacing:1.3px; text-transform:uppercase; font-weight:700; color:var(--mj-red);
  margin-bottom:5px }
.mj-ua-result__t{ display:block; font-family:var(--font-display); font-weight:700; font-size:1.02rem;
  line-height:1.25; color:var(--mj-black); margin-bottom:4px }
body.dark-mode .mj-ua-result__t{ color:#fff }
.mj-ua-result:hover .mj-ua-result__t{ color:var(--mj-trust) }
.mj-ua-result__d{ display:block; font-size:.88rem; line-height:1.55; color:var(--mj-text-muted) }
.mj-ua-result mark{ background:var(--mj-growth-subtle); color:inherit; padding:0 2px; border-radius:1px }
body.dark-mode .mj-ua-result mark{ background:rgba(16,185,129,.2) }

/* empty state — what a search with no results should actually do */
.mj-ua-empty{ padding:var(--sp-7) var(--sp-5); text-align:center; border:1px dashed var(--mj-rule);
  border-radius:var(--radius-md) }
body.dark-mode .mj-ua-empty{ border-color:#2a2a2d }
.mj-ua-empty b{ display:block; font-family:var(--font-display); font-weight:800; font-size:1.1rem;
  color:var(--mj-black); margin-bottom:8px }
body.dark-mode .mj-ua-empty b{ color:#fff }
.mj-ua-empty p{ margin:0 auto var(--sp-4); max-width:44ch; font-size:.94rem; line-height:1.6;
  color:var(--mj-text-muted) }

/* =============================================================================
   WAVE A — MONETIZATION DEPTH  (.mj-mz*)
   product-price · affiliate-link · affiliate-offer · smartcms-pick
   · product-alternative · product-vendor · comparison

   ⚑ THE SCHEMA'S BIGGEST REVEAL: `product_price_variations_fc` has FIVE layouts
   (📦 Standard · 🔄 Subscription · 🎁 Bundle · 🛠️ Service · 🔌 API) and EVERY ONE of them
   carries its own `country_region` (taxonomy) AND `currency` (select: ZAR/USD/EUR/GBP…),
   plus before_discount / discount_percent / discount_description / after_discount.

   That means: **a price is not a number. It is a number, in a currency, in a region,
   under a discount, at a point in time.** The multi-country/multi-currency layer Lucky
   asked for is not a future feature — it is already the shape of the field. Any design
   that renders a bare "R699" is throwing four dimensions away.
   ============================================================================= */

/* THE PRICE ENTITY — one product, many regional variations */
.mj-mz-price{ margin:var(--sp-6) 0; border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  overflow:hidden; background:var(--mj-surface) }
body.dark-mode .mj-mz-price{ background:#131315; border-color:#2a2a2d }
.mj-mz-price__head{ display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-3) var(--sp-4);
  background:var(--mj-ink-900); color:#fff; font-family:var(--font-meta); font-size:.66rem;
  letter-spacing:1.5px; text-transform:uppercase; font-weight:700 }
.mj-mz-price__type{ margin-left:auto; opacity:.7 }
.mj-mz-row{ display:grid; grid-template-columns:auto 1fr auto; gap:var(--sp-4); align-items:center;
  padding:var(--sp-4); border-top:1px solid var(--mj-rule); transition:background .16s ease }
.mj-mz-row:first-of-type{ border-top:0 }
body.dark-mode .mj-mz-row{ border-top-color:#2a2a2d }
.mj-mz-row:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-mz-row:hover{ background:#1b1b1e }
/* region + currency — the two dimensions a bare price throws away */
.mj-mz-region{ display:inline-flex; align-items:center; gap:7px; font-family:var(--font-meta);
  font-size:.62rem; letter-spacing:1.1px; text-transform:uppercase; font-weight:700;
  color:var(--mj-text-muted); white-space:nowrap }
.mj-mz-region__flag{ width:20px; height:14px; border-radius:1px; flex:0 0 auto;
  background:var(--mj-surface-2); display:inline-flex; align-items:center; justify-content:center;
  font-size:.5rem; color:var(--mj-gray-400); border:1px solid var(--mj-rule) }
.mj-mz-cur{ font-family:var(--font-display); font-weight:800; font-size:.62rem;
  color:var(--mj-gray-400); letter-spacing:.5px }
.mj-mz-amt{ display:flex; align-items:baseline; gap:9px; flex-wrap:wrap }
.mj-mz-amt b{ font-family:var(--font-display); font-weight:800; font-size:1.35rem;
  color:var(--mj-black); line-height:1 }
body.dark-mode .mj-mz-amt b{ color:#fff }
.mj-mz-amt s{ font-size:.9rem; color:var(--mj-gray-400) }
.mj-mz-off{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1px; text-transform:uppercase;
  font-weight:700; color:#08694c; background:var(--mj-growth-subtle); padding:2px 7px; border-radius:2px }
body.dark-mode .mj-mz-off{ background:rgba(16,185,129,.14); color:#34d8a6 }
.mj-mz-desc{ display:block; font-size:.8rem; color:var(--mj-text-muted); margin-top:4px }
.mj-mz-when{ font-family:var(--font-meta); font-size:.58rem; letter-spacing:1px; text-transform:uppercase;
  color:var(--mj-gray-400); white-space:nowrap; text-align:right }
/* the primary region gets a marker — which price the reader sees by default */
.mj-mz-row--primary{ background:var(--mj-growth-subtle) }
body.dark-mode .mj-mz-row--primary{ background:rgba(16,185,129,.07) }
.mj-mz-row--primary:hover{ background:#d8f5ea }
body.dark-mode .mj-mz-row--primary:hover{ background:rgba(16,185,129,.13) }

/* THE LINK REGISTRY — affiliate-link as an entity, not a URL typed in prose */
.mj-mz-link{ display:grid; grid-template-columns:1fr auto; gap:var(--sp-4); align-items:center;
  padding:var(--sp-4); border:1px solid var(--mj-rule); border-left:4px solid var(--mj-red);
  border-radius:0 var(--radius-md) var(--radius-md) 0; background:var(--mj-surface);
  margin:var(--sp-4) 0 }
body.dark-mode .mj-mz-link{ background:#131315; border-color:#2a2a2d; border-left-color:var(--mj-red) }
.mj-mz-link__u{ font-family:var(--font-display); font-weight:700; font-size:.86rem;
  color:var(--mj-black); word-break:break-all }
body.dark-mode .mj-mz-link__u{ color:#fff }
.mj-mz-link__m{ display:block; font-family:var(--font-meta); font-size:.58rem; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-gray-400); margin-top:5px }
.mj-mz-link__count{ font-family:var(--font-display); font-weight:800; font-size:1.5rem;
  color:var(--mj-black); text-align:right; line-height:1 }
body.dark-mode .mj-mz-link__count{ color:#fff }
.mj-mz-link__count span{ display:block; font-family:var(--font-meta); font-size:.55rem;
  letter-spacing:1.1px; text-transform:uppercase; color:var(--mj-gray-400); font-weight:700; margin-top:5px }

/* EDITOR'S PICK — a curated verdict, distinct from a ranked list */
.mj-mz-pick{ display:grid; grid-template-columns:auto 1fr; gap:var(--sp-5); align-items:start;
  padding:var(--sp-5); border:1px solid var(--mj-rule); border-top:3px solid var(--mj-growth);
  border-radius:0 0 var(--radius-md) var(--radius-md); background:var(--mj-surface);
  margin:var(--sp-6) 0 }
body.dark-mode .mj-mz-pick{ background:#131315; border-color:#2a2a2d; border-top-color:var(--mj-growth) }
.mj-mz-pick__seal{ display:flex; flex-direction:column; align-items:center; justify-content:center;
  width:78px; padding:var(--sp-3); border:1px solid var(--mj-growth); border-radius:2px;
  background:var(--mj-growth-subtle); text-align:center; flex:0 0 auto }
body.dark-mode .mj-mz-pick__seal{ background:rgba(16,185,129,.1) }
.mj-mz-pick__seal b{ font-family:var(--font-display); font-weight:800; font-size:.66rem;
  letter-spacing:.5px; text-transform:uppercase; color:#08694c; line-height:1.2 }
body.dark-mode .mj-mz-pick__seal b{ color:#34d8a6 }
.mj-mz-pick__seal span{ font-family:var(--font-meta); font-size:.5rem; letter-spacing:.8px;
  text-transform:uppercase; color:#08694c; margin-top:5px }
body.dark-mode .mj-mz-pick__seal span{ color:#34d8a6 }
.mj-mz-pick__t{ font-family:var(--font-display); font-weight:800; font-size:1.12rem; margin:0 0 6px;
  color:var(--mj-black) }
body.dark-mode .mj-mz-pick__t{ color:#fff }
.mj-mz-pick__why{ font-size:.94rem; line-height:1.6; color:var(--mj-text-muted); margin:0 0 var(--sp-3) }

/* ALTERNATIVES — targeted by brand/country/industry/funnel */
.mj-mz-alt{ display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:2px;
  background:var(--mj-rule); border:1px solid var(--mj-rule); margin:var(--sp-5) 0 }
body.dark-mode .mj-mz-alt{ background:#2a2a2d; border-color:#2a2a2d }
.mj-mz-alt__i{ background:var(--mj-surface); padding:var(--sp-4); text-decoration:none; display:block;
  transition:background .16s ease }
body.dark-mode .mj-mz-alt__i{ background:#131315 }
.mj-mz-alt__i:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-mz-alt__i:hover{ background:#1b1b1e }
.mj-mz-alt__k{ display:block; font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.3px;
  text-transform:uppercase; font-weight:700; color:var(--mj-red); margin-bottom:6px }
.mj-mz-alt__n{ display:block; font-family:var(--font-display); font-weight:800; font-size:.98rem;
  color:var(--mj-black); margin-bottom:4px }
body.dark-mode .mj-mz-alt__n{ color:#fff }
.mj-mz-alt__w{ display:block; font-size:.84rem; line-height:1.5; color:var(--mj-text-muted) }

/* VENDOR — where to buy, and who they are */
.mj-mz-vendor{ display:grid; grid-template-columns:auto 1fr auto; gap:var(--sp-4); align-items:center;
  padding:var(--sp-4); border-top:1px solid var(--mj-rule) }
.mj-mz-vendor:first-of-type{ border-top:0 }
body.dark-mode .mj-mz-vendor{ border-top-color:#2a2a2d }
.mj-mz-vendor__logo{ width:52px; height:34px; object-fit:contain; border-radius:2px;
  background:var(--mj-surface-2); padding:4px; flex:0 0 auto }
body.dark-mode .mj-mz-vendor__logo{ background:#1a1a1d }
.mj-mz-vendor__n{ font-family:var(--font-display); font-weight:700; font-size:.98rem;
  color:var(--mj-black); margin:0 }
body.dark-mode .mj-mz-vendor__n{ color:#fff }
.mj-mz-vendor__d{ font-size:.84rem; color:var(--mj-text-muted); margin:3px 0 0 }
@media(max-width:620px){
  .mj-mz-row{ grid-template-columns:1fr; gap:8px }
  .mj-mz-when{ text-align:left }
  .mj-mz-link{ grid-template-columns:1fr }
  .mj-mz-link__count{ text-align:left }
  .mj-mz-pick{ grid-template-columns:1fr }
}

/* =============================================================================
   WAVE B — FUNNEL & CAPTURE  (.mj-fn*)
   lead-magnet · digital-kit · smart-physical-kit · smart-partner-bundle ·
   download · smart-micro-funnel · smartcms-newsletters · smartcms-popup ·
   conversion-tools · smart-whatsapp-group
   Design authority, Opus 5, 2026-08-05. v3 L0 — element CSS lives here only.

   THE GOVERNING IDEA: a reader who will not buy today is not a lost reader,
   they are an UNPRICED one. Every element below is an honest trade — it names
   what the reader gets, what it costs them, and what arrives afterwards,
   BEFORE they hand anything over. No square is a circle; no gate is hidden.
   ============================================================================= */

/* --- AUDIENCE TARGETING STRIP — the taxonomy layer made visible ------------- */
.mj-fn-target{ display:flex; flex-wrap:wrap; align-items:center; gap:6px;
  padding:var(--sp-3) var(--sp-4); background:var(--mj-surface-2);
  border:1px solid var(--mj-rule); border-left:3px solid var(--mj-navy); margin:var(--sp-4) 0 }
body.dark-mode .mj-fn-target{ background:#151517; border-color:#2a2a2d; border-left-color:#4a6da7 }
.mj-fn-target__l{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.4px;
  text-transform:uppercase; color:var(--mj-gray-400); font-weight:700; margin-right:4px }
.mj-fn-target__t{ font-family:var(--font-meta); font-size:.66rem; font-weight:700;
  letter-spacing:.4px; padding:3px 8px; background:var(--mj-surface); color:var(--mj-text-muted);
  border:1px solid var(--mj-rule) }
body.dark-mode .mj-fn-target__t{ background:#1b1b1e; border-color:#33333a; color:#c9c9cf }
.mj-fn-target__t b{ color:var(--mj-navy); font-weight:800 }
body.dark-mode .mj-fn-target__t b{ color:#7fa6e0 }
.mj-fn-target__t--off{ opacity:.5; text-decoration:line-through }

/* --- THE PROMISE LINE — mandatory on every capture ------------------------- */
.mj-fn-promise{ display:flex; flex-wrap:wrap; gap:var(--sp-4); padding:var(--sp-3) 0 0;
  margin-top:var(--sp-3); border-top:1px solid var(--mj-rule);
  font-family:var(--font-meta); font-size:.68rem; color:var(--mj-gray-400); line-height:1.6 }
body.dark-mode .mj-fn-promise{ border-top-color:#2a2a2d }
.mj-fn-promise span{ display:flex; align-items:baseline; gap:5px }
.mj-fn-promise b{ color:var(--mj-text-muted); font-weight:700 }
body.dark-mode .mj-fn-promise b{ color:#c9c9cf }

/* --- LEAD MAGNET — the trade ---------------------------------------------- */
.mj-fn-magnet{ border:1px solid var(--mj-rule); border-top:3px solid var(--mj-red);
  background:var(--mj-surface); padding:var(--sp-5); margin:var(--sp-6) 0 }
body.dark-mode .mj-fn-magnet{ background:#131315; border-color:#2a2a2d; border-top-color:var(--mj-red) }
.mj-fn-magnet__k{ display:inline-block; font-family:var(--font-meta); font-size:.56rem;
  letter-spacing:1.4px; text-transform:uppercase; font-weight:700; color:#fff;
  background:var(--mj-red); padding:3px 8px; margin-bottom:var(--sp-3) }
.mj-fn-magnet__t{ font-family:var(--font-display); font-weight:800; font-size:1.24rem;
  line-height:1.2; margin:0 0 8px; color:var(--mj-black) }
body.dark-mode .mj-fn-magnet__t{ color:#fff }
.mj-fn-magnet__d{ font-size:.95rem; line-height:1.65; color:var(--mj-text-muted); margin:0 0 var(--sp-4) }
.mj-fn-magnet__trade{ display:grid; grid-template-columns:1fr 1fr; gap:1px;
  background:var(--mj-rule); border:1px solid var(--mj-rule); margin:0 0 var(--sp-4) }
body.dark-mode .mj-fn-magnet__trade{ background:#2a2a2d; border-color:#2a2a2d }
.mj-fn-magnet__side{ background:var(--mj-surface-2); padding:var(--sp-4) }
body.dark-mode .mj-fn-magnet__side{ background:#1a1a1d }
.mj-fn-magnet__side h4{ font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.4px;
  text-transform:uppercase; color:var(--mj-gray-400); font-weight:700; margin:0 0 8px }
.mj-fn-magnet__side ul{ margin:0; padding:0; list-style:none }
.mj-fn-magnet__side li{ font-size:.86rem; line-height:1.55; color:var(--mj-ink);
  padding-left:16px; position:relative; margin-bottom:5px }
body.dark-mode .mj-fn-magnet__side li{ color:#dcdce0 }
.mj-fn-magnet__side li::before{ content:'—'; position:absolute; left:0; color:var(--mj-gray-400) }
.mj-fn-magnet__side--get li::before{ content:'✓'; color:var(--mj-growth-text); font-weight:700 }
.mj-fn-form{ display:flex; gap:8px; flex-wrap:wrap }
.mj-fn-form input{ flex:1 1 220px; font-family:var(--font-body); font-size:.94rem;
  padding:11px 13px; border:1px solid var(--mj-gray-300); background:var(--mj-surface);
  color:var(--mj-ink); border-radius:0 }
body.dark-mode .mj-fn-form input{ background:#1b1b1e; border-color:#3a3a42; color:#eee }
.mj-fn-form button{ font-family:var(--font-display); font-weight:800; font-size:.84rem;
  letter-spacing:.4px; text-transform:uppercase; padding:11px 20px; border:0; cursor:pointer;
  background:var(--mj-red); color:#fff }
.mj-fn-form button:hover{ background:var(--mj-red-hover) }

/* --- THE GATE — gated_mode made honest ------------------------------------ */
.mj-fn-gate{ display:flex; align-items:flex-start; gap:10px; padding:var(--sp-3) var(--sp-4);
  background:var(--mj-alert-subtle); border:1px solid #e8d5a8; border-left:3px solid #b8860b;
  font-family:var(--font-meta); font-size:.72rem; line-height:1.6; color:#6b4e0a; margin:var(--sp-3) 0 }
body.dark-mode .mj-fn-gate{ background:rgba(184,134,11,.12); border-color:#5c4a1e;
  border-left-color:#c9a227; color:#e0c477 }
.mj-fn-gate b{ font-weight:800 }
.mj-fn-gate--open{ background:var(--mj-growth-subtle); border-color:#b6e6d5;
  border-left-color:var(--mj-growth); color:#08694c }
body.dark-mode .mj-fn-gate--open{ background:rgba(16,185,129,.1); border-color:#1e5c48;
  border-left-color:var(--mj-growth); color:#34d8a6 }

/* --- DIGITAL KIT — the asset manifest ------------------------------------- */
.mj-fn-kit{ border:1px solid var(--mj-rule); background:var(--mj-surface); margin:var(--sp-6) 0 }
body.dark-mode .mj-fn-kit{ background:#131315; border-color:#2a2a2d }
.mj-fn-kit__h{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-4);
  flex-wrap:wrap; padding:var(--sp-4) var(--sp-5); background:var(--mj-ink-900); color:#fff }
.mj-fn-kit__h h3{ font-family:var(--font-display); font-weight:800; font-size:1.1rem; margin:0 }
.mj-fn-kit__c{ font-family:var(--font-meta); font-size:.66rem; letter-spacing:1.2px;
  text-transform:uppercase; color:#9a9aa2 }
.mj-fn-kit__c b{ color:#fff; font-weight:800 }
.mj-fn-kit__body{ padding:var(--sp-5) }
.mj-fn-asset{ display:grid; grid-template-columns:auto 1fr auto; gap:var(--sp-3) var(--sp-4);
  align-items:start; padding:var(--sp-3) 0; border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-fn-asset{ border-bottom-color:#2a2a2d }
.mj-fn-asset:last-child{ border-bottom:0 }
.mj-fn-asset__n{ font-family:var(--font-display); font-weight:800; font-size:.8rem;
  color:var(--mj-gray-400); min-width:22px; padding-top:2px }
.mj-fn-asset__t{ font-family:var(--font-display); font-weight:700; font-size:.98rem;
  color:var(--mj-black); margin:0 0 3px }
body.dark-mode .mj-fn-asset__t{ color:#fff }
.mj-fn-asset__p{ font-size:.86rem; line-height:1.55; color:var(--mj-text-muted); margin:0 }
.mj-fn-asset__g{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:.8px;
  text-transform:uppercase; font-weight:700; padding:3px 8px; white-space:nowrap;
  border:1px solid var(--mj-rule); color:var(--mj-text-muted); background:var(--mj-surface-2) }
body.dark-mode .mj-fn-asset__g{ background:#1b1b1e; border-color:#33333a; color:#c9c9cf }
.mj-fn-asset__g--email{ border-color:#b8860b; color:#6b4e0a; background:var(--mj-alert-subtle) }
body.dark-mode .mj-fn-asset__g--email{ background:rgba(184,134,11,.12); border-color:#5c4a1e; color:#e0c477 }
.mj-fn-asset__g--aff{ border-color:var(--mj-red-subtle); color:var(--mj-red); background:#fff5f5 }
body.dark-mode .mj-fn-asset__g--aff{ background:rgba(236,34,39,.1); border-color:#5c2226; color:#ff8a8f }

/* --- PHYSICAL KIT --------------------------------------------------------- */
.mj-fn-pkit{ display:grid; grid-template-columns:150px 1fr; gap:var(--sp-5);
  border:1px solid var(--mj-rule); background:var(--mj-surface); padding:var(--sp-5);
  margin:var(--sp-6) 0 }
body.dark-mode .mj-fn-pkit{ background:#131315; border-color:#2a2a2d }
.mj-fn-pkit__ph{ aspect-ratio:1/1; display:flex; align-items:center; justify-content:center;
  text-align:center; background:var(--mj-surface-2); border:1px dashed var(--mj-gray-300);
  font-family:var(--font-meta); font-size:.6rem; letter-spacing:1px; text-transform:uppercase;
  color:var(--mj-gray-400); padding:8px }
body.dark-mode .mj-fn-pkit__ph{ background:#1b1b1e; border-color:#3a3a42 }
.mj-fn-pkit__items{ margin:var(--sp-3) 0 0; padding:0; list-style:none;
  border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-fn-pkit__items{ border-top-color:#2a2a2d }
.mj-fn-pkit__items li{ padding:8px 0 8px 18px; border-bottom:1px solid var(--mj-rule);
  position:relative; font-size:.9rem; line-height:1.5; color:var(--mj-ink) }
body.dark-mode .mj-fn-pkit__items li{ border-bottom-color:#2a2a2d; color:#dcdce0 }
.mj-fn-pkit__items li::before{ content:''; position:absolute; left:0; top:14px; width:7px; height:7px;
  background:var(--mj-red) }
.mj-fn-pkit__items li em{ display:block; font-style:normal; font-family:var(--font-meta);
  font-size:.72rem; color:var(--mj-gray-400); margin-top:2px }
@media(max-width:560px){ .mj-fn-pkit{ grid-template-columns:1fr } }

/* --- PARTNER BUNDLE ------------------------------------------------------- */
.mj-fn-bundle{ border:1px solid var(--mj-rule); border-top:3px solid var(--mj-navy);
  background:var(--mj-surface); margin:var(--sp-6) 0 }
body.dark-mode .mj-fn-bundle{ background:#131315; border-color:#2a2a2d; border-top-color:#4a6da7 }
.mj-fn-bundle__h{ padding:var(--sp-5) var(--sp-5) var(--sp-4);
  border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-fn-bundle__h{ border-bottom-color:#2a2a2d }
.mj-fn-bundle__h h3{ font-family:var(--font-display); font-weight:800; font-size:1.2rem;
  margin:0 0 6px; color:var(--mj-black) }
body.dark-mode .mj-fn-bundle__h h3{ color:#fff }
.mj-fn-bundle__h p{ margin:0; font-size:.92rem; line-height:1.6; color:var(--mj-text-muted) }
.mj-fn-offer{ display:grid; grid-template-columns:auto 1fr; gap:var(--sp-4);
  padding:var(--sp-4) var(--sp-5); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-fn-offer{ border-bottom-color:#2a2a2d }
.mj-fn-offer__b{ width:9px; height:9px; background:var(--mj-navy); margin-top:6px }
body.dark-mode .mj-fn-offer__b{ background:#7fa6e0 }
.mj-fn-offer__t{ font-family:var(--font-display); font-weight:700; font-size:1rem;
  margin:0 0 4px; color:var(--mj-black) }
body.dark-mode .mj-fn-offer__t{ color:#fff }
.mj-fn-offer__d{ margin:0; font-size:.88rem; line-height:1.6; color:var(--mj-text-muted) }
.mj-fn-bundle__f{ display:flex; align-items:center; justify-content:space-between; gap:var(--sp-4);
  flex-wrap:wrap; padding:var(--sp-4) var(--sp-5); background:var(--mj-surface-2) }
body.dark-mode .mj-fn-bundle__f{ background:#1a1a1d }

/* --- DOWNLOAD ROW --------------------------------------------------------- */
.mj-fn-file{ display:flex; align-items:center; gap:var(--sp-4); flex-wrap:wrap;
  padding:var(--sp-4); border:1px solid var(--mj-rule); background:var(--mj-surface);
  margin:var(--sp-3) 0 }
body.dark-mode .mj-fn-file{ background:#131315; border-color:#2a2a2d }
.mj-fn-file__x{ font-family:var(--font-display); font-weight:800; font-size:.68rem;
  letter-spacing:.6px; padding:7px 9px; background:var(--mj-ink-900); color:#fff; flex:0 0 auto }
.mj-fn-file__t{ flex:1 1 200px; font-family:var(--font-display); font-weight:700; font-size:.98rem;
  color:var(--mj-black); margin:0 }
body.dark-mode .mj-fn-file__t{ color:#fff }
.mj-fn-file__m{ font-family:var(--font-meta); font-size:.68rem; color:var(--mj-gray-400) }

/* --- MICRO FUNNEL — the conversion sequence -------------------------------- */
.mj-fn-seq{ counter-reset:fnstep; margin:var(--sp-6) 0 }
.mj-fn-seq__bar{ display:flex; gap:3px; margin-bottom:var(--sp-4) }
.mj-fn-seq__bar i{ flex:1; height:4px; background:var(--mj-gray-200) }
body.dark-mode .mj-fn-seq__bar i{ background:#2a2a2d }
.mj-fn-seq__bar i.on{ background:var(--mj-red) }
.mj-fn-step{ display:grid; grid-template-columns:auto 1fr; gap:var(--sp-4); align-items:start;
  padding:var(--sp-4) 0; border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-fn-step{ border-bottom-color:#2a2a2d }
.mj-fn-step:last-child{ border-bottom:0 }
.mj-fn-step__n{ counter-increment:fnstep; font-family:var(--font-display); font-weight:800;
  font-size:.74rem; letter-spacing:.5px; width:30px; height:30px; display:flex;
  align-items:center; justify-content:center; background:var(--mj-ink-900); color:#fff;
  flex:0 0 auto }
.mj-fn-step__n::before{ content:counter(fnstep) }
.mj-fn-step__t{ font-family:var(--font-display); font-weight:800; font-size:1rem; margin:0 0 4px;
  color:var(--mj-black) }
body.dark-mode .mj-fn-step__t{ color:#fff }
.mj-fn-step__blk{ display:inline-block; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:1px; text-transform:uppercase; font-weight:700; color:var(--mj-text-muted);
  border:1px solid var(--mj-rule); background:var(--mj-surface-2); padding:2px 7px; margin-bottom:6px }
body.dark-mode .mj-fn-step__blk{ background:#1b1b1e; border-color:#33333a; color:#c9c9cf }
.mj-fn-step__p{ margin:0; font-size:.9rem; line-height:1.6; color:var(--mj-text-muted) }

/* --- NEWSLETTER ----------------------------------------------------------- */
.mj-fn-news{ border:1px solid var(--mj-rule); background:var(--mj-surface); margin:var(--sp-6) 0 }
body.dark-mode .mj-fn-news{ background:#131315; border-color:#2a2a2d }
.mj-fn-news__h{ padding:var(--sp-4) var(--sp-5); border-bottom:1px solid var(--mj-rule);
  display:flex; align-items:baseline; gap:var(--sp-3); flex-wrap:wrap }
body.dark-mode .mj-fn-news__h{ border-bottom-color:#2a2a2d }
.mj-fn-news__badge{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; padding:3px 8px; background:var(--mj-navy); color:#fff }
.mj-fn-news__sub{ font-family:var(--font-display); font-weight:800; font-size:1.06rem;
  margin:0; color:var(--mj-black); flex:1 1 240px }
body.dark-mode .mj-fn-news__sub{ color:#fff }
.mj-fn-news__d{ font-family:var(--font-meta); font-size:.68rem; color:var(--mj-gray-400) }
.mj-fn-news__body{ padding:var(--sp-5) }
.mj-fn-news__body > p{ margin:0 0 var(--sp-4); font-size:.94rem; line-height:1.7;
  color:var(--mj-ink) }
body.dark-mode .mj-fn-news__body > p{ color:#dcdce0 }
.mj-fn-news__list{ margin:0; padding:0; list-style:none; border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-fn-news__list{ border-top-color:#2a2a2d }
.mj-fn-news__list li{ padding:10px 0; border-bottom:1px solid var(--mj-rule);
  display:flex; gap:var(--sp-3); align-items:baseline }
body.dark-mode .mj-fn-news__list li{ border-bottom-color:#2a2a2d }
.mj-fn-news__list em{ font-style:normal; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:1px; text-transform:uppercase; color:var(--mj-gray-400); flex:0 0 78px }

/* --- POPUP — shown as a specimen, never as a live interruption ------------- */
.mj-fn-popwrap{ position:relative; border:1px dashed var(--mj-gray-300);
  background:var(--mj-surface-2); padding:var(--sp-7) var(--sp-5); margin:var(--sp-6) 0;
  display:flex; justify-content:center }
body.dark-mode .mj-fn-popwrap{ background:#161618; border-color:#3a3a42 }
.mj-fn-popwrap::before{ content:'Specimen — rendered inline, not fired'; position:absolute;
  top:8px; left:10px; font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--mj-gray-400) }
.mj-fn-pop{ width:100%; max-width:400px; background:var(--mj-surface);
  border:1px solid var(--mj-rule); box-shadow:0 14px 44px rgba(0,0,0,.16); padding:var(--sp-5) }
body.dark-mode .mj-fn-pop{ background:#1b1b1e; border-color:#33333a }
.mj-fn-pop__x{ float:right; font-family:var(--font-meta); font-size:1rem; line-height:1;
  color:var(--mj-gray-400); cursor:pointer; padding:2px 5px; border:1px solid var(--mj-rule) }
.mj-fn-pop__t{ font-family:var(--font-display); font-weight:800; font-size:1.12rem;
  margin:0 0 8px; color:var(--mj-black) }
body.dark-mode .mj-fn-pop__t{ color:#fff }
.mj-fn-pop__m{ font-size:.9rem; line-height:1.6; color:var(--mj-text-muted); margin:0 0 var(--sp-4) }
.mj-fn-trig{ width:100%; border-collapse:collapse; margin:var(--sp-4) 0; font-size:.86rem }
.mj-fn-trig th,.mj-fn-trig td{ text-align:left; padding:9px 12px;
  border-bottom:1px solid var(--mj-rule); vertical-align:top }
body.dark-mode .mj-fn-trig th,body.dark-mode .mj-fn-trig td{ border-bottom-color:#2a2a2d }
.mj-fn-trig th{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--mj-gray-400); font-weight:700 }
.mj-fn-trig td{ color:var(--mj-ink) }
body.dark-mode .mj-fn-trig td{ color:#dcdce0 }
.mj-fn-trig tr.no td{ background:#fff5f5 }
body.dark-mode .mj-fn-trig tr.no td{ background:rgba(236,34,39,.08) }
.mj-fn-trig tr.yes td{ background:var(--mj-growth-subtle) }
body.dark-mode .mj-fn-trig tr.yes td{ background:rgba(16,185,129,.08) }
.mj-fn-trig td b{ font-weight:800 }

/* --- WHATSAPP GROUP ------------------------------------------------------- */
.mj-fn-wa{ border:1px solid var(--mj-rule); border-left:3px solid #128C7E;
  background:var(--mj-surface); padding:var(--sp-5); margin:var(--sp-6) 0 }
body.dark-mode .mj-fn-wa{ background:#131315; border-color:#2a2a2d; border-left-color:#25D366 }
.mj-fn-wa__t{ font-family:var(--font-display); font-weight:800; font-size:1.1rem; margin:0 0 6px;
  color:var(--mj-black) }
body.dark-mode .mj-fn-wa__t{ color:#fff }
.mj-fn-wa__msg{ font-family:var(--font-meta); font-size:.86rem; line-height:1.6;
  padding:var(--sp-3) var(--sp-4); background:#e7f7ef; border:1px solid #bfe6d5;
  color:#0d5c4f; margin:var(--sp-3) 0 }
body.dark-mode .mj-fn-wa__msg{ background:rgba(37,211,102,.09); border-color:#1e5c48; color:#7fe0b4 }

/* --- CONVERSION TOOL ------------------------------------------------------ */
.mj-fn-tool{ border:1px solid var(--mj-rule); border-top:3px solid var(--mj-trust);
  background:var(--mj-surface); padding:var(--sp-5); margin:var(--sp-6) 0 }
body.dark-mode .mj-fn-tool{ background:#131315; border-color:#2a2a2d; border-top-color:#5b8def }
.mj-fn-tool__t{ font-family:var(--font-display); font-weight:800; font-size:1.12rem;
  margin:0 0 6px; color:var(--mj-black) }
body.dark-mode .mj-fn-tool__t{ color:#fff }
.mj-fn-tool__row{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
  padding:var(--sp-3) 0; border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-fn-tool__row{ border-bottom-color:#2a2a2d }
.mj-fn-tool__row label{ flex:1 1 180px; font-size:.9rem; color:var(--mj-ink) }
body.dark-mode .mj-fn-tool__row label{ color:#dcdce0 }
.mj-fn-tool__row input{ width:130px; font-family:var(--font-body); font-size:.94rem;
  padding:8px 10px; border:1px solid var(--mj-gray-300); background:var(--mj-surface);
  color:var(--mj-ink) }
body.dark-mode .mj-fn-tool__row input{ background:#1b1b1e; border-color:#3a3a42; color:#eee }
.mj-fn-tool__out{ display:flex; align-items:baseline; justify-content:space-between;
  gap:var(--sp-4); flex-wrap:wrap; padding:var(--sp-4); margin-top:var(--sp-4);
  background:var(--mj-trust-subtle); border:1px solid #cfe2ff }
body.dark-mode .mj-fn-tool__out{ background:rgba(37,99,235,.1); border-color:#254070 }
.mj-fn-tool__out b{ font-family:var(--font-display); font-weight:800; font-size:1.5rem;
  color:var(--mj-black) }
body.dark-mode .mj-fn-tool__out b{ color:#fff }
.mj-fn-tool__out span{ font-family:var(--font-meta); font-size:.7rem; color:var(--mj-text-muted) }
body.dark-mode .mj-fn-tool__out span{ color:#c9c9cf }

/* =============================================================================
   WAVE C — EDITORIAL DEPTH  (.mj-ed*, + smart-callout variant completion)
   standard-paragraph · article-introduction · verdict-paragraph · smart-callout ·
   smart-feature-block · smart-guide · guides-multimedia · article-brief · testimonials
   Design authority, Opus 5, 2026-08-05. v3 L0 — element CSS lives here only.

   THE GOVERNING IDEA: the paragraph CPTs are not paragraphs. Each is a SECTION —
   three prose beats with an insert slot after every one, plus recursive
   sub-sections. That shape IS the reading-experience contract (R4 insert floors,
   R5 "compression is relocation, not deletion") expressed as a field group.
   The design's whole job is to make the slots legible without making them loud.
   ============================================================================= */

/* --- SMART-CALLOUT: the 6 schema visual_style values -----------------------
   .mj-callout already exists (default red · --green · --blue). These complete
   the set against `visual_style` and MUST NOT change the three in use. */
.mj-callout--tip{ border-left-color:var(--mj-growth); background:var(--mj-growth-subtle) }
.mj-callout--tip .mj-callout__label{ color:#08694c }
body.dark-mode .mj-callout--tip{ background:rgba(16,185,129,.10) }
body.dark-mode .mj-callout--tip .mj-callout__label{ color:#34d8a6 }
.mj-callout--warning{ border-left-color:#b8860b; background:var(--mj-alert-subtle) }
.mj-callout--warning .mj-callout__label{ color:#6b4e0a }
body.dark-mode .mj-callout--warning{ background:rgba(184,134,11,.12) }
body.dark-mode .mj-callout--warning .mj-callout__label{ color:#e0c477 }
.mj-callout--info{ border-left-color:var(--mj-trust); background:var(--mj-trust-subtle) }
.mj-callout--info .mj-callout__label{ color:var(--mj-trust) }
body.dark-mode .mj-callout--info{ background:rgba(37,99,235,.10) }
body.dark-mode .mj-callout--info .mj-callout__label{ color:#7fa6e0 }
.mj-callout--question{ border-left-color:var(--mj-navy); background:var(--mj-surface-2) }
.mj-callout--question .mj-callout__label{ color:var(--mj-navy) }
body.dark-mode .mj-callout--question .mj-callout__label{ color:#7fa6e0 }
.mj-callout--stat{ border-left-color:var(--mj-ink-900); background:var(--mj-surface-2);
  display:grid; grid-template-columns:auto 1fr; gap:var(--sp-5); align-items:center }
.mj-callout--stat .mj-callout__n{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(1.9rem,5vw,2.8rem); line-height:1; letter-spacing:-.02em;
  color:var(--mj-black); white-space:nowrap }
body.dark-mode .mj-callout--stat .mj-callout__n{ color:#fff }
.mj-callout--stat .mj-callout__src{ display:block; margin-top:6px; font-family:var(--font-meta);
  font-size:.66rem; color:var(--mj-gray-400) }
/* layout_style modifiers — inline is the default and carries no class */
.mj-callout--boxed{ border:1px solid var(--mj-rule); border-left-width:6px }
body.dark-mode .mj-callout--boxed{ border-color:#2a2a2d }
.mj-callout--sidebar{ padding:var(--sp-4); font-size:.9rem }
.mj-callout--banner{ border-left:0; border-top:4px solid var(--mj-red); border-radius:0;
  width:min(var(--wide), calc(100vw - 2*var(--sp-5))); margin-left:50%; transform:translateX(-50%) }
.mj-callout--banner.mj-callout--tip{ border-top-color:var(--mj-growth) }
.mj-callout--banner.mj-callout--info{ border-top-color:var(--mj-trust) }
.mj-callout--banner.mj-callout--warning{ border-top-color:#b8860b }

/* --- THE SECTION — standard-paragraph / article-introduction / verdict-paragraph */
/* THE SECTION OWNS THE RHYTHM; ITS CONTENTS DO NOT ADD TO IT.
   Lucky, on the gap after "The short answer" and before the black summary strip:
   "the gaps between those sections are a little bit too much or out of balance
    compared to the rest."

   He was reading a real inconsistency. `display:flow-root` stops margins
   collapsing out of the section, which is what keeps the 48px rhythm honest
   between sections — but it also means a child's OWN margin stacks INSIDE the
   box on top of that 48. A section led by a heading was flush, because the
   heading has margin-top 0; a section holding a single insert added the
   insert's 24-32px at both ends. So the short answer was followed by 48 + 48 +
   32 = 128px while every heading-led section sat at 48, and the eye reads the
   difference long before anyone measures it.

   One rule, and it holds for every insert that will ever be dropped into a
   section on its own: the first child starts flush and the last child ends
   flush. Spacing between sections is the section's job, exactly once.

   THE FIRST CHILD IS OFTEN AN EMPTY ANCHOR SPAN — the jump target for the
   in-page nav — so `:first-child` alone zeroes a margin that is already zero
   and leaves the visible insert's intact. That is why the second selector is
   here, and why the first attempt at this fix appeared to do nothing. */
.mj-ed-sec{ display:flow-root; margin:var(--sp-7) 0 }
.mj-ed-sec > :first-child,
.mj-ed-sec > span:first-child + *{ margin-top:0 }
.mj-ed-sec > :last-child{ margin-bottom:0 }
.mj-ed-sec__h{ font-family:var(--font-display); font-weight:800; font-size:1.36rem;
  line-height:1.18; letter-spacing:-.01em; margin:0 0 var(--sp-3); color:var(--mj-black) }
body.dark-mode .mj-ed-sec__h{ color:#fff }
/* AN H3 HAS TO LOOK LIKE ONE (2026-09-11). Both tags shared 1.36rem, so a
   section written as an H3 read as a sibling of the H2 above it. Lucky, on
   "Brand new? It comes down to two" under "So which one should you buy?":
   "make this heading an H3" — it already was one in the markup, and nothing on
   the page could show it. The step is one size down, not a different design. */
h3.mj-ed-sec__h{ font-size:1.14rem; line-height:1.24 }
/* PROSE PARAGRAPHS ONLY — the DIRECT children of the section.
   ------------------------------------------------------------------------
   This was `.mj-ed-sec p`, a blanket descendant rule, and it silently outranked
   EVERY ELEMENT'S OWN TYPOGRAPHY inside an article. `.mj-ed-sec p` scores
   (0,1,1); `.scms-rb-heading` scores (0,1,0), so a review block's product name
   rendered in the body serif instead of Technor — and so did the eyebrow, the
   one-line verdict, the fit cells, the price note and the callout body.

   FIFTY-NINE element paragraphs on a single article were being overridden.
   That is the bulk of what Lucky saw as "the text styles are not fully applied
   in them": the elements were bound, extracted, gated and correct, and a single
   descendant selector two hundred lines away was quietly restyling their
   insides.

   The walker emits prose as DIRECT children of the section and every element as
   a nested container, so `>` separates them exactly. A blanket descendant rule
   inside a container that holds designed elements is never safe — it cannot
   tell prose from an element, and it always wins.
   ------------------------------------------------------------------------ */
.mj-ed-sec > p{ font-family:var(--font-body); font-size:1.02rem; line-height:1.78;
  color:var(--mj-ink); margin:0 0 var(--sp-4) }
.mj-ed-sec__role{ display:inline-flex; align-items:center; gap:6px; font-family:var(--font-meta);
  font-size:.58rem; letter-spacing:1.5px; text-transform:uppercase; font-weight:700;
  padding:3px 8px; margin-bottom:var(--sp-2); color:#fff; background:var(--mj-ink-900) }
.mj-ed-sec__role--intro{ background:var(--mj-trust) }
.mj-ed-sec__role--verdict{ background:var(--mj-red) }
/* the insert slot — visible in the prototype, invisible in production */
.mj-ed-slot{ border:1px dashed var(--mj-gray-300); background:var(--mj-surface-2);
  padding:var(--sp-3) var(--sp-4); margin:var(--sp-4) 0; display:flex; align-items:center;
  gap:var(--sp-3); flex-wrap:wrap }
body.dark-mode .mj-ed-slot{ background:#17171a; border-color:#3a3a42 }
.mj-ed-slot__l{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.4px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400); flex:0 0 auto }
.mj-ed-slot__t{ font-family:var(--font-meta); font-size:.66rem; font-weight:700;
  padding:3px 8px; background:var(--mj-surface); border:1px solid var(--mj-rule);
  color:var(--mj-text-muted) }
body.dark-mode .mj-ed-slot__t{ background:#1b1b1e; border-color:#33333a; color:#c9c9cf }
.mj-ed-slot__t--used{ background:var(--mj-ink-900); border-color:var(--mj-ink-900); color:#fff }
.mj-ed-slot--empty{ opacity:.55 }
/* sub-paragraph — the recursion, indented once and once only */
.mj-ed-sub{ margin:var(--sp-5) 0 var(--sp-5) var(--sp-5); padding-left:var(--sp-5);
  border-left:2px solid var(--mj-rule) }
body.dark-mode .mj-ed-sub{ border-left-color:#2a2a2d }
.mj-ed-sub__h{ font-family:var(--font-display); font-weight:800; font-size:1.06rem;
  margin:0 0 var(--sp-2); color:var(--mj-black) }
body.dark-mode .mj-ed-sub__h{ color:#fff }
/* the bulleted variant */
.mj-ed-bul{ margin:0 0 var(--sp-4); padding:0; list-style:none }
.mj-ed-bul li{ position:relative; padding-left:20px; margin-bottom:10px;
  font-size:1rem; line-height:1.7; color:var(--mj-ink) }
.mj-ed-bul li::before{ content:''; position:absolute; left:0; top:11px; width:7px; height:7px;
  background:var(--mj-red) }

/* --- FEATURE BLOCK -------------------------------------------------------- */
.mj-ed-feat{ display:grid; grid-template-columns:auto 1fr; gap:var(--sp-4);
  border:1px solid var(--mj-rule); background:var(--mj-surface); padding:var(--sp-5);
  margin:var(--sp-5) 0 }
body.dark-mode .mj-ed-feat{ background:#131315; border-color:#2a2a2d }
.mj-ed-feat__i{ width:40px; height:40px; display:flex; align-items:center; justify-content:center;
  font-size:1.3rem; background:var(--mj-surface-2); border:1px solid var(--mj-rule); flex:0 0 auto }
body.dark-mode .mj-ed-feat__i{ background:#1b1b1e; border-color:#33333a }
.mj-ed-feat__h{ font-family:var(--font-display); font-weight:800; font-size:1.06rem;
  margin:0 0 3px; color:var(--mj-black) }
body.dark-mode .mj-ed-feat__h{ color:#fff }
.mj-ed-feat__s{ font-family:var(--font-meta); font-size:.72rem; letter-spacing:.4px;
  color:var(--mj-gray-400); margin:0 0 8px }
.mj-ed-feat__d{ font-size:.92rem; line-height:1.65; color:var(--mj-text-muted); margin:0 }
.mj-ed-feat--grid{ margin:0 }
.mj-ed-featgrid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:2px;
  background:var(--mj-rule); border:1px solid var(--mj-rule); margin:var(--sp-5) 0 }
body.dark-mode .mj-ed-featgrid{ background:#2a2a2d; border-color:#2a2a2d }
.mj-ed-featgrid .mj-ed-feat{ border:0 }
.mj-ed-feat--side{ grid-template-columns:1fr 1fr; align-items:center }
.mj-ed-feat--min{ grid-template-columns:1fr; border:0; border-top:1px solid var(--mj-rule);
  padding:var(--sp-4) 0; margin:0 }
body.dark-mode .mj-ed-feat--min{ border-top-color:#2a2a2d; background:transparent }
@media(max-width:560px){ .mj-ed-feat--side{ grid-template-columns:1fr } }

/* --- SMART GUIDE — the reusable step sequence ------------------------------ */
.mj-ed-guide{ border:1px solid var(--mj-rule); background:var(--mj-surface); margin:var(--sp-6) 0 }
body.dark-mode .mj-ed-guide{ background:#131315; border-color:#2a2a2d }
.mj-ed-guide__h{ padding:var(--sp-5); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-ed-guide__h{ border-bottom-color:#2a2a2d }
.mj-ed-guide__h h3{ font-family:var(--font-display); font-weight:800; font-size:1.2rem;
  margin:0 0 6px; color:var(--mj-black) }
body.dark-mode .mj-ed-guide__h h3{ color:#fff }
.mj-ed-guide__h p{ margin:0; font-size:.94rem; line-height:1.65; color:var(--mj-text-muted) }
.mj-ed-gstep{ display:grid; grid-template-columns:34px 1fr; gap:var(--sp-4);
  padding:var(--sp-5); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-ed-gstep{ border-bottom-color:#2a2a2d }
.mj-ed-gstep__n{ width:34px; height:34px; display:flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; font-size:.86rem;
  background:var(--mj-ink-900); color:#fff }
.mj-ed-gstep__t{ font-family:var(--font-display); font-weight:800; font-size:1.04rem;
  margin:0 0 6px; color:var(--mj-black) }
body.dark-mode .mj-ed-gstep__t{ color:#fff }
.mj-ed-gstep__d{ margin:0 0 var(--sp-3); font-size:.94rem; line-height:1.68;
  color:var(--mj-text-muted) }
.mj-ed-gstep__lay{ display:inline-block; font-family:var(--font-meta); font-size:.58rem;
  letter-spacing:1.2px; text-transform:uppercase; font-weight:700; padding:2px 7px;
  border:1px solid var(--mj-rule); color:var(--mj-gray-400); margin-bottom:8px }
body.dark-mode .mj-ed-gstep__lay{ border-color:#33333a }
.mj-ed-guide__out{ padding:var(--sp-5); background:var(--mj-surface-2) }
body.dark-mode .mj-ed-guide__out{ background:#1a1a1d }

/* --- MULTIMEDIA — labelled-pending frame (G7) ------------------------------ */
.mj-ed-mm{ border:1px dashed var(--mj-gray-300); background:var(--mj-surface-2);
  aspect-ratio:16/9; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:8px; text-align:center; padding:var(--sp-4); margin:var(--sp-4) 0 }
body.dark-mode .mj-ed-mm{ background:#17171a; border-color:#3a3a42 }
.mj-ed-mm__k{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.4px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400) }
.mj-ed-mm__t{ font-family:var(--font-display); font-weight:700; font-size:.98rem;
  color:var(--mj-text-muted); margin:0 }
.mj-ed-mm__tr{ font-family:var(--font-meta); font-size:.68rem; color:var(--mj-gray-400);
  max-width:44ch; line-height:1.6 }

/* --- TESTIMONIAL — proof first -------------------------------------------- */
.mj-ed-test{ border:1px solid var(--mj-rule); border-left:3px solid var(--mj-growth);
  background:var(--mj-surface); padding:var(--sp-5); margin:var(--sp-5) 0 }
body.dark-mode .mj-ed-test{ background:#131315; border-color:#2a2a2d; border-left-color:var(--mj-growth) }
.mj-ed-test--noproof{ border-left-color:var(--mj-gray-300) }
.mj-ed-test__q{ font-family:var(--font-body); font-size:1.06rem; line-height:1.7;
  color:var(--mj-ink); margin:0 0 var(--sp-4) }
.mj-ed-test__who{ display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding-top:var(--sp-3); border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-ed-test__who{ border-top-color:#2a2a2d }
.mj-ed-test__n{ font-family:var(--font-display); font-weight:800; font-size:.92rem;
  color:var(--mj-black) }
body.dark-mode .mj-ed-test__n{ color:#fff }
.mj-ed-test__r{ font-family:var(--font-meta); font-size:.72rem; color:var(--mj-gray-400) }
.mj-ed-test__proof{ display:inline-flex; align-items:center; gap:5px; margin-left:auto;
  font-family:var(--font-meta); font-size:.62rem; letter-spacing:.8px; text-transform:uppercase;
  font-weight:700; padding:3px 8px; background:var(--mj-growth-subtle); color:#08694c;
  border:1px solid #b6e6d5 }
body.dark-mode .mj-ed-test__proof{ background:rgba(16,185,129,.1); border-color:#1e5c48; color:#34d8a6 }
.mj-ed-test__proof--none{ background:var(--mj-surface-2); color:var(--mj-gray-400);
  border-color:var(--mj-rule) }
body.dark-mode .mj-ed-test__proof--none{ background:#1b1b1e; border-color:#33333a }
.mj-ed-test__shot{ display:block; margin-top:var(--sp-3); border:1px solid var(--mj-rule);
  background:var(--mj-surface-2); padding:var(--sp-5); text-align:center;
  font-family:var(--font-meta); font-size:.66rem; letter-spacing:1px; text-transform:uppercase;
  color:var(--mj-gray-400) }
body.dark-mode .mj-ed-test__shot{ background:#1b1b1e; border-color:#33333a }

/* --- ARTICLE BRIEF — the ONLY Wave C element with no reader-facing render --- */
.mj-ed-brief{ border:2px solid var(--mj-navy); background:var(--mj-surface); margin:var(--sp-6) 0;
  font-family:var(--font-meta) }
body.dark-mode .mj-ed-brief{ background:#121216; border-color:#4a6da7 }
.mj-ed-brief__h{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-4);
  flex-wrap:wrap; padding:var(--sp-3) var(--sp-4); background:var(--mj-navy); color:#fff }
.mj-ed-brief__h b{ font-family:var(--font-display); font-weight:800; font-size:.94rem }
.mj-ed-brief__h span{ font-size:.62rem; letter-spacing:1.4px; text-transform:uppercase; opacity:.75 }
.mj-ed-brief__row{ display:grid; grid-template-columns:26px 1fr; gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-4); border-bottom:1px solid var(--mj-rule); align-items:start }
body.dark-mode .mj-ed-brief__row{ border-bottom-color:#2a2a2d }
.mj-ed-brief__box{ width:16px; height:16px; border:2px solid var(--mj-gray-300); margin-top:3px }
.mj-ed-brief__box--on{ background:var(--mj-growth); border-color:var(--mj-growth); position:relative }
.mj-ed-brief__box--on::after{ content:'✓'; position:absolute; inset:0; color:#fff;
  font-size:.66rem; font-weight:700; display:flex; align-items:center; justify-content:center }
.mj-ed-brief__t{ font-family:var(--font-display); font-weight:700; font-size:.9rem;
  color:var(--mj-black); margin:0 0 3px }
body.dark-mode .mj-ed-brief__t{ color:#fff }
.mj-ed-brief__d{ margin:0; font-size:.78rem; line-height:1.6; color:var(--mj-text-muted) }
.mj-ed-brief__ai{ display:block; margin-top:6px; padding:6px 9px; background:var(--mj-surface-2);
  border-left:2px solid var(--mj-gray-300); font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:.7rem; line-height:1.55; color:var(--mj-text-muted) }
body.dark-mode .mj-ed-brief__ai{ background:#1b1b1e; border-left-color:#3a3a42 }

/* =============================================================================
   WAVE D — SITE FURNITURE  (.mj-sf*, + navblock layout completion)
   navblock · smartcms-sidebar · smartcms-top-bar · content-hub · autolink ·
   smart-access-rule · packaging-image
   Design authority, Opus 5, 2026-08-05. v3 L0 — element CSS lives here only.

   THE GOVERNING IDEA: furniture is what the reader uses without noticing. Every
   element here earns its place by MOVING someone — to the next page, the right
   page, or past a gate — and each must be quieter than the content it surrounds.
   Nothing in this wave may out-shout a heading.
   ============================================================================= */

/* --- NAVBLOCK — the four layout_style values ------------------------------- */
.mj-navblock__intro{ font-size:.92rem; line-height:1.65; color:var(--mj-text-muted);
  margin:0 0 var(--sp-3) }
.mj-navblock__list{ margin:0; padding:0; list-style:none }
.mj-nb-link{ display:grid; grid-template-columns:auto 1fr; gap:10px; align-items:start;
  padding:10px 0; border-bottom:1px solid var(--mj-rule); text-decoration:none }
body.dark-mode .mj-nb-link{ border-bottom-color:#2a2a2d }
.mj-nb-link:last-child{ border-bottom:0 }
.mj-nb-link__i{ font-size:.95rem; line-height:1.5; width:20px; text-align:center; flex:0 0 auto }
.mj-nb-link__t{ font-family:var(--font-display); font-weight:700; font-size:.96rem;
  color:var(--mj-navy); margin:0 0 2px }
body.dark-mode .mj-nb-link__t{ color:#a9c4ea }
.mj-nb-link:hover .mj-nb-link__t{ color:var(--mj-red) }
.mj-nb-link__d{ margin:0; font-size:.82rem; line-height:1.55; color:var(--mj-text-muted) }
.mj-nb-link__x{ display:inline-block; margin-left:6px; font-family:var(--font-meta);
  font-size:.56rem; letter-spacing:1px; text-transform:uppercase; color:var(--mj-gray-400);
  vertical-align:middle }
.mj-navblock--grid .mj-navblock__list{ display:grid;
  grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:var(--sp-4) }
.mj-navblock--grid .mj-nb-link{ border-bottom:0; border-top:2px solid var(--mj-rule);
  padding:var(--sp-3) 0 0 }
body.dark-mode .mj-navblock--grid .mj-nb-link{ border-top-color:#2a2a2d }
.mj-navblock--mega{ border-left:0; border-top:3px solid var(--mj-red);
  width:min(var(--wide), calc(100vw - 2*var(--sp-5))); margin-left:50%; transform:translateX(-50%) }
.mj-navblock--mega .mj-navblock__list{ display:grid;
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:var(--sp-3) var(--sp-5) }
.mj-navblock--mega .mj-nb-link{ border-bottom:0; padding:6px 0 }

/* --- NAVIGATION ANCHORS — the auto-generator, position by position ---------- */
.mj-sf-anchors{ border:1px solid var(--mj-rule); background:var(--mj-surface); margin:var(--sp-6) 0 }
body.dark-mode .mj-sf-anchors{ background:#131315; border-color:#2a2a2d }
.mj-sf-anchor{ display:grid; grid-template-columns:150px 1fr auto; gap:var(--sp-4);
  align-items:center; padding:var(--sp-3) var(--sp-4); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-sf-anchor{ border-bottom-color:#2a2a2d }
.mj-sf-anchor:last-child{ border-bottom:0 }
.mj-sf-anchor__p{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:1.3px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400) }
.mj-sf-anchor__c{ font-family:var(--font-display); font-weight:700; font-size:.92rem;
  color:var(--mj-black) }
body.dark-mode .mj-sf-anchor__c{ color:#fff }
.mj-sf-anchor__id{ font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:.72rem;
  padding:3px 7px; background:var(--mj-surface-2); border:1px solid var(--mj-rule);
  color:var(--mj-text-muted); white-space:nowrap }
body.dark-mode .mj-sf-anchor__id{ background:#1b1b1e; border-color:#33333a; color:#c9c9cf }
.mj-sf-anchor--beat{ background:var(--mj-trust-subtle) }
body.dark-mode .mj-sf-anchor--beat{ background:rgba(37,99,235,.08) }
@media(max-width:560px){ .mj-sf-anchor{ grid-template-columns:1fr } }

/* --- SIDEBAR — the sticky rail -------------------------------------------- */
.mj-sf-side{ position:sticky; top:var(--sp-5); border:1px solid var(--mj-rule);
  background:var(--mj-surface); padding:var(--sp-4) }
body.dark-mode .mj-sf-side{ background:#131315; border-color:#2a2a2d }
.mj-sf-side__h{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.5px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400);
  padding-bottom:8px; margin:0 0 var(--sp-3); border-bottom:2px solid var(--mj-black) }
body.dark-mode .mj-sf-side__h{ border-bottom-color:#fff }
.mj-sf-side__slot{ padding:var(--sp-3) 0; border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-sf-side__slot{ border-bottom-color:#2a2a2d }
.mj-sf-side__slot:last-child{ border-bottom:0 }
.mj-sf-side__k{ display:block; font-family:var(--font-meta); font-size:.54rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:var(--mj-red); margin-bottom:5px }
.mj-sf-side__t{ font-family:var(--font-display); font-weight:700; font-size:.9rem; line-height:1.35;
  color:var(--mj-black); margin:0 0 3px }
body.dark-mode .mj-sf-side__t{ color:#fff }
.mj-sf-side__m{ font-family:var(--font-meta); font-size:.68rem; color:var(--mj-gray-400) }

/* --- TOP BAR -------------------------------------------------------------- */
.mj-sf-top{ display:flex; align-items:center; justify-content:center; gap:var(--sp-4);
  flex-wrap:wrap; padding:9px var(--sp-5); background:var(--mj-ink-900); color:#fff;
  font-family:var(--font-meta); font-size:.78rem; line-height:1.5;
  width:min(var(--wide), calc(100vw - 2*var(--sp-5))); margin:var(--sp-5) 0 var(--sp-5) 50%;
  transform:translateX(-50%) }
.mj-sf-top b{ font-weight:800 }
.mj-sf-top a{ color:#fff; text-decoration:underline; text-underline-offset:3px }
.mj-sf-top__x{ margin-left:var(--sp-3); cursor:pointer; border:1px solid #4a4a52;
  padding:1px 7px; font-size:.8rem; line-height:1.3; color:#c9c9cf }
.mj-sf-top--highlight{ background:var(--mj-red) }
.mj-sf-top--highlight .mj-sf-top__x{ border-color:rgba(255,255,255,.45); color:#fff }
.mj-sf-top__d{ font-size:.62rem; letter-spacing:1px; text-transform:uppercase; opacity:.62 }

/* --- CONTENT HUB ---------------------------------------------------------- */
.mj-sf-hub{ border:1px solid var(--mj-rule); background:var(--mj-surface); margin:var(--sp-6) 0 }
body.dark-mode .mj-sf-hub{ background:#131315; border-color:#2a2a2d }
.mj-sf-hub__h{ padding:var(--sp-5); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-sf-hub__h{ border-bottom-color:#2a2a2d }
.mj-sf-hub__k{ display:inline-block; font-family:var(--font-meta); font-size:.58rem;
  letter-spacing:1.4px; text-transform:uppercase; font-weight:700; padding:3px 8px;
  background:var(--mj-navy); color:#fff; margin-bottom:var(--sp-3) }
.mj-sf-hub__k--promo{ background:var(--mj-red) }
.mj-sf-hub__h h3{ font-family:var(--font-display); font-weight:800; font-size:1.24rem;
  margin:0 0 6px; color:var(--mj-black) }
body.dark-mode .mj-sf-hub__h h3{ color:#fff }
.mj-sf-hub__h p{ margin:0; font-size:.94rem; line-height:1.65; color:var(--mj-text-muted) }
.mj-sf-hub__grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:1px;
  background:var(--mj-rule) }
body.dark-mode .mj-sf-hub__grid{ background:#2a2a2d }
.mj-sf-hub__cell{ background:var(--mj-surface); padding:var(--sp-4) }
body.dark-mode .mj-sf-hub__cell{ background:#131315 }
.mj-sf-hub__ct{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400); display:block; margin-bottom:8px }
.mj-sf-hub__cell ul{ margin:0; padding:0; list-style:none }
.mj-sf-hub__cell li{ font-size:.86rem; line-height:1.5; padding:5px 0;
  border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-sf-hub__cell li{ border-bottom-color:#2a2a2d }
.mj-sf-hub__cell li:last-child{ border-bottom:0 }
.mj-sf-hub__n{ display:inline-block; min-width:22px; font-family:var(--font-display);
  font-weight:800; font-size:.72rem; color:var(--mj-red) }

/* --- ACCESS RULE — the gate matrix ---------------------------------------- */
.mj-sf-gate{ border:1px solid var(--mj-rule); border-top:3px solid var(--mj-navy);
  background:var(--mj-surface); padding:var(--sp-5); margin:var(--sp-5) 0 }
body.dark-mode .mj-sf-gate{ background:#131315; border-color:#2a2a2d; border-top-color:#4a6da7 }
.mj-sf-gate__t{ font-family:var(--font-display); font-weight:800; font-size:1.04rem;
  margin:0 0 8px; color:var(--mj-black) }
body.dark-mode .mj-sf-gate__t{ color:#fff }
.mj-sf-gate__m{ font-size:.92rem; line-height:1.65; color:var(--mj-text-muted); margin:0 }
.mj-sf-gate--teaser{ position:relative; overflow:hidden; max-height:170px }
.mj-sf-gate--teaser::after{ content:''; position:absolute; left:0; right:0; bottom:0; height:90px;
  background:linear-gradient(transparent, var(--mj-surface)) }
body.dark-mode .mj-sf-gate--teaser::after{ background:linear-gradient(transparent,#131315) }
.mj-sf-rules{ width:100%; border-collapse:collapse; margin:var(--sp-4) 0; font-size:.86rem }
.mj-sf-rules th,.mj-sf-rules td{ text-align:left; padding:9px 12px; vertical-align:top;
  border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-sf-rules th,body.dark-mode .mj-sf-rules td{ border-bottom-color:#2a2a2d }
.mj-sf-rules th{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--mj-gray-400); font-weight:700 }
.mj-sf-rules td{ color:var(--mj-ink) }
body.dark-mode .mj-sf-rules td{ color:#dcdce0 }
.mj-sf-rules tr.no td{ background:#fff5f5 }
body.dark-mode .mj-sf-rules tr.no td{ background:rgba(236,34,39,.08) }
.mj-sf-rules tr.yes td{ background:var(--mj-growth-subtle) }
body.dark-mode .mj-sf-rules tr.yes td{ background:rgba(16,185,129,.08) }

/* --- AUTOLINK — the marked-up specimen ------------------------------------ */
.mj-sf-auto{ border:1px solid var(--mj-rule); background:var(--mj-surface); padding:var(--sp-5);
  margin:var(--sp-5) 0; font-family:var(--font-body); font-size:1rem; line-height:1.85;
  color:var(--mj-ink) }
body.dark-mode .mj-sf-auto{ background:#131315; border-color:#2a2a2d; color:#dcdce0 }
.mj-sf-auto mark{ background:linear-gradient(transparent 60%, rgba(37,99,235,.22) 60%);
  color:inherit; padding:0 1px; position:relative }
.mj-sf-auto mark::after{ content:attr(data-t); position:relative; top:-.55em; left:2px;
  font-family:var(--font-meta); font-size:.52rem; letter-spacing:.6px; text-transform:uppercase;
  color:var(--mj-trust); font-weight:700 }
.mj-sf-auto mark.gl{ background:linear-gradient(transparent 60%, rgba(16,185,129,.24) 60%) }
.mj-sf-auto mark.gl::after{ color:#08694c }
body.dark-mode .mj-sf-auto mark.gl::after{ color:#34d8a6 }
.mj-sf-auto mark.no{ background:linear-gradient(transparent 60%, rgba(236,34,39,.2) 60%) }
.mj-sf-auto mark.no::after{ color:var(--mj-red) }

/* --- PACKAGING IMAGE — labelled-pending (G7) ------------------------------ */
.mj-sf-pkg{ display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr));
  gap:var(--sp-3); margin:var(--sp-5) 0 }
.mj-sf-pkg__c{ aspect-ratio:1/1; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:6px; text-align:center; padding:var(--sp-3);
  border:1px dashed var(--mj-gray-300); background:var(--mj-surface-2) }
body.dark-mode .mj-sf-pkg__c{ background:#17171a; border-color:#3a3a42 }
.mj-sf-pkg__k{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400) }
.mj-sf-pkg__t{ font-family:var(--font-display); font-weight:700; font-size:.84rem;
  color:var(--mj-text-muted); margin:0 }

/* =============================================================================
   PHASE 1 CONSOLIDATION (Opus 5, 2026-08-05)
   ============================================================================= */

/* --- CURATOR LINE — attribution for INDEX surfaces (homepage, archives) ------
   An article has a .mj-byline: a person made this claim. An index has a CURATOR
   line: a desk chose and ordered these, and last checked them on a date.
   Different promise, so a different element — never reuse the byline here.
   The date is the point. An index with no review date is a claim that nothing
   on it has gone stale, made silently. */
.mj-curator{ display:flex; align-items:baseline; gap:var(--sp-4); flex-wrap:wrap;
  padding:var(--sp-3) 0; border-top:1px solid var(--mj-rule);
  border-bottom:1px solid var(--mj-rule); margin:var(--sp-4) 0;
  font-family:var(--font-meta); font-size:.72rem; line-height:1.6; color:var(--mj-text-muted) }
body.dark-mode .mj-curator{ border-color:#2a2a2d; color:#a4a4aa }
.mj-curator__who{ display:flex; align-items:center; gap:8px }
.mj-curator__who b{ font-family:var(--font-display); font-weight:800; font-size:.78rem;
  color:var(--mj-black) }
body.dark-mode .mj-curator__who b{ color:#fff }
.mj-curator__m{ display:flex; align-items:baseline; gap:5px }
.mj-curator__m b{ font-weight:800; color:var(--mj-black) }
body.dark-mode .mj-curator__m b{ color:#fff }
.mj-curator__count{ margin-left:auto; font-size:.68rem; color:var(--mj-gray-400) }
@media(max-width:560px){ .mj-curator__count{ margin-left:0; width:100% } }

/* =============================================================================
   PHASE 2 — THE HERO & THUMBNAIL SYSTEM  (.mj-hero*, .mj-thumb*)
   Design authority, Opus 5, 2026-08-05. v3 L0.

   THE RULING: a hero image promises SPECTACLE; a typographic opening promises
   AUTHORITY. For a desk whose position is "we buy the tools, test the fees and
   publish the date we checked", THE CREDIBILITY ASSET IS THE NUMBER AND THE
   DATE, NOT A PHOTOGRAPH. So type-led is the DEFAULT, and an image has to earn
   its place by doing one of three things a headline cannot:
        show the product · show many products fairly · orient before an argument

   FOUR TIERS, assigned by article_type:
     H0 Typographic  680px, no image   Explainer, FAQ
     H1 Inset        680px, 3:2        Business Guide, How-To
     H2 Wide         960px, 16:9       Product Review, Deals
     H3 Mosaic       960px, cut-outs   List, Comparison

   THE BRAND SIGNATURE: every hero carries a VERIFICATION STAMP — photographed
   by us, dated, and labelled when it is not ours. A caption reading
   "photographed in our office, 7 Jul 2026" is worth more to this brand than a
   beautiful image, because it is the one hero treatment a competitor who does
   not buy the products CANNOT COPY.
   ============================================================================= */

/* --- shared ---------------------------------------------------------------- */
.mj-hero{ margin:var(--sp-5) 0 var(--sp-6) }
.mj-hero__frame{ position:relative; display:flex; align-items:center; justify-content:center;
  overflow:hidden; background:var(--mj-surface-2); border:1px solid var(--mj-rule) }
body.dark-mode .mj-hero__frame{ background:#1b1b1e; border-color:#2a2a2d }
.mj-hero__frame img{ width:100%; height:100%; object-fit:cover; display:block }
/* labelled-pending (G7) — never a blurry stand-in, never stock pretending to be ours */
.mj-hero__frame--pending{ border-style:dashed; border-color:var(--mj-gray-300);
  flex-direction:column; gap:6px; text-align:center; padding:var(--sp-5) }
body.dark-mode .mj-hero__frame--pending{ border-color:#3a3a42 }
.mj-hero__ph{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:1.4px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400) }
.mj-hero__phd{ font-family:var(--font-meta); font-size:.72rem; color:var(--mj-gray-400);
  max-width:40ch; line-height:1.6 }

/* the caption row — the caption is editorial, the stamp is evidence */
.mj-hero__cap{ display:flex; align-items:baseline; gap:var(--sp-4); flex-wrap:wrap;
  padding-top:9px; font-family:var(--font-meta); font-size:.72rem; line-height:1.6;
  color:var(--mj-text-muted) }
body.dark-mode .mj-hero__cap{ color:#a4a4aa }
.mj-hero__cap p{ margin:0; flex:1 1 260px }

/* THE VERIFICATION STAMP — the thing competitors cannot copy */
.mj-hero__stamp{ display:inline-flex; align-items:center; gap:6px; flex:0 0 auto;
  font-family:var(--font-meta); font-size:.62rem; letter-spacing:.9px; text-transform:uppercase;
  font-weight:700; padding:3px 9px; border:1px solid var(--mj-rule); background:var(--mj-surface-2);
  color:var(--mj-text-muted) }
body.dark-mode .mj-hero__stamp{ background:#1b1b1e; border-color:#33333a; color:#c9c9cf }
/* ours — we bought it, we shot it, here is the date */
.mj-hero__stamp--ours{ background:var(--mj-growth-subtle); border-color:#b6e6d5; color:#08694c }
body.dark-mode .mj-hero__stamp--ours{ background:rgba(16,185,129,.1); border-color:#1e5c48; color:#34d8a6 }
/* supplier — allowed, and ALWAYS labelled. An unlabelled supplier render is never allowed. */
.mj-hero__stamp--supplier{ background:var(--mj-alert-subtle); border-color:#e8d5a8; color:#6b4e0a }
body.dark-mode .mj-hero__stamp--supplier{ background:rgba(184,134,11,.12); border-color:#5c4a1e; color:#e0c477 }

/* H1 · INSET — inside the measure. Orientation without pre-emption. */
.mj-hero--inset .mj-hero__frame{ aspect-ratio:3/2 }

/* H2 · WIDE — breaks the measure. The product IS the subject.
   The safe break-out: max-width alone cannot widen a child past a 680px parent,
   and a bare width would scroll at 375px. This does neither. */
.mj-hero--wide{ width:min(var(--wide), calc(100vw - 2*var(--sp-5)));
  margin-left:50%; transform:translateX(-50%) }
.mj-hero--wide .mj-hero__frame{ aspect-ratio:16/9 }
.mj-hero--wide .mj-hero__cap{ max-width:var(--article-width); margin:0 auto }

/* H3 · MOSAIC — many subjects. A single hero would rank one before a word is
   read, which is a ranking error committed in the image. */
.mj-hero--mosaic{ width:min(var(--wide), calc(100vw - 2*var(--sp-5)));
  margin-left:50%; transform:translateX(-50%) }
.mj-hero__grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:2px;
  background:var(--mj-rule); border:1px solid var(--mj-rule) }
body.dark-mode .mj-hero__grid{ background:#2a2a2d; border-color:#2a2a2d }
.mj-hero__cut{ aspect-ratio:1/1; background:#fff; display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:5px; text-align:center; padding:var(--sp-3) }
.mj-hero__cut img{ width:100%; height:100%; object-fit:contain }
.mj-hero__cut b{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.1px;
  text-transform:uppercase; color:var(--mj-gray-400); font-weight:700 }
.mj-hero__cut span{ font-family:var(--font-display); font-weight:700; font-size:.76rem;
  color:var(--mj-text-muted); line-height:1.3 }
.mj-hero--mosaic .mj-hero__cap{ max-width:var(--article-width); margin:0 auto }
@media(max-width:560px){
  .mj-hero--wide, .mj-hero--mosaic{ width:calc(100vw - 2*var(--sp-4)) }
  .mj-hero__grid{ grid-template-columns:repeat(2,1fr) }
}

/* --- THUMBNAILS — one vocabulary for every card surface -------------------- */
.mj-thumb{ position:relative; display:flex; align-items:center; justify-content:center;
  overflow:hidden; background:var(--mj-surface-2); border:1px solid var(--mj-rule) }
body.dark-mode .mj-thumb{ background:#1b1b1e; border-color:#2a2a2d }
.mj-thumb img{ width:100%; height:100%; object-fit:cover; display:block }
.mj-thumb--lead{ aspect-ratio:16/9 }
.mj-thumb--card{ aspect-ratio:4/3 }
.mj-thumb--rail{ aspect-ratio:1/1; width:64px; flex:0 0 64px }
.mj-thumb--pending{ border-style:dashed; border-color:var(--mj-gray-300) }
body.dark-mode .mj-thumb--pending{ border-color:#3a3a42 }
.mj-thumb__ph{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400); text-align:center; padding:6px }
/* a product cut-out keeps a white ground in BOTH themes */
.mj-thumb--cutout{ background:#fff }
.mj-thumb--cutout img{ object-fit:contain; padding:8px }

/* =============================================================================
   PHASE 4 — HONEST ROTATION + THE EXPANSION SECTIONS  (.mj-rot*, .mj-start,
   .mj-changed, .mj-answered, .mj-biz, .mj-progress)
   Design authority, Opus 5, 2026-08-05. v3 L0.

   THE ROTATION DOCTRINE — WE ROTATE THE POOL, NOT THE PERSON.
     No cookies. No profile. No stored behaviour. No per-reader state at all.
     Every reader sees the same page at the same moment; the freshness comes
     from TIME, not from TRACKING.
       seed = floor(hoursSinceEpoch / 8)   → 3 rotations a day, deterministic
     Why this is BETTER and not merely safer: a shared rotation is fully
     cacheable (critical for a static site), explainable in one sentence, and
     impossible to get creepily wrong. A personal rotation would need
     surveillance to deliver an effect we can get from a clock.

     THE EDITORIAL WEIGHT OF A POSITION NEVER CHANGES. A "start here" slot
     always shows a start-here article. Only the MEMBERS rotate. Structure is
     preserved; the reader's expectation of the page is never violated.

     AND IT IS STATED ON THE PAGE. A rotation the reader understands is a
     rotation they trust.
   ============================================================================= */

/* --- the rotation wrapper -------------------------------------------------- */
/* No-JS fallback: set 1 shows. JS adds .is-live, then .is-on to the seeded set. */
.mj-rot__set{ display:none }
.mj-rot__set:first-of-type{ display:block }
.mj-rot.is-live .mj-rot__set{ display:none }
.mj-rot.is-live .mj-rot__set.is-on{ display:block }
.mj-rot__note{ display:flex; align-items:center; gap:7px; margin-top:var(--sp-3);
  font-family:var(--font-meta); font-size:.66rem; color:var(--mj-gray-400); line-height:1.6 }
.mj-rot__note b{ color:var(--mj-text-muted); font-weight:700 }
.mj-rot__dot{ width:6px; height:6px; background:var(--mj-growth); flex:0 0 auto }

/* --- F · START HERE — the on-ramp a first-time reader has never had --------- */
.mj-start{ display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:2px;
  background:var(--mj-rule); border:1px solid var(--mj-rule) }
body.dark-mode .mj-start{ background:#2a2a2d; border-color:#2a2a2d }
.mj-start__c{ background:var(--mj-surface); padding:var(--sp-4); text-decoration:none; display:block }
body.dark-mode .mj-start__c{ background:#131315 }
.mj-start__c:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-start__c:hover{ background:#1b1b1e }
.mj-start__n{ display:inline-block; font-family:var(--font-display); font-weight:800; font-size:.66rem;
  letter-spacing:1px; padding:2px 7px; background:var(--mj-ink-900); color:#fff; margin-bottom:9px }
.mj-start__t{ font-family:var(--font-display); font-weight:800; font-size:1rem; line-height:1.25;
  margin:0 0 5px; color:var(--mj-black) }
body.dark-mode .mj-start__t{ color:#fff }
.mj-start__c:hover .mj-start__t{ color:var(--mj-red) }
.mj-start__d{ margin:0; font-size:.85rem; line-height:1.55; color:var(--mj-text-muted) }
.mj-start__m{ display:block; margin-top:8px; font-family:var(--font-meta); font-size:.62rem;
  color:var(--mj-gray-400) }

/* --- G · WE CHANGED OUR MIND — nothing else on the internet publishes this -- */
.mj-changed{ border:1px solid var(--mj-rule); border-left:4px solid var(--mj-red);
  background:var(--mj-surface); margin:var(--sp-5) 0 }
body.dark-mode .mj-changed{ background:#131315; border-color:#2a2a2d; border-left-color:var(--mj-red) }
.mj-changed__i{ display:grid; grid-template-columns:1fr auto 1fr; gap:var(--sp-4);
  align-items:center; padding:var(--sp-4) var(--sp-5); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-changed__i{ border-bottom-color:#2a2a2d }
.mj-changed__i:last-child{ border-bottom:0 }
.mj-changed__was,.mj-changed__now{ min-width:0 }
.mj-changed__l{ display:block; font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.3px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400); margin-bottom:4px }
.mj-changed__v{ font-family:var(--font-display); font-weight:800; font-size:.98rem; line-height:1.3;
  color:var(--mj-black) }
body.dark-mode .mj-changed__v{ color:#fff }
.mj-changed__was .mj-changed__v{ color:var(--mj-gray-400); text-decoration:line-through }
.mj-changed__arrow{ font-family:var(--font-display); font-weight:800; color:var(--mj-red);
  font-size:1.1rem }
.mj-changed__why{ grid-column:1/-1; margin:6px 0 0; font-size:.86rem; line-height:1.6;
  color:var(--mj-text-muted) }
.mj-changed__why b{ color:var(--mj-black); font-weight:700 }
body.dark-mode .mj-changed__why b{ color:#fff }
@media(max-width:560px){ .mj-changed__i{ grid-template-columns:1fr }
  .mj-changed__arrow{ transform:rotate(90deg); width:fit-content } }

/* --- H · ANSWERED — freshness without needing a new article ---------------- */
.mj-answered{ margin:0; padding:0; list-style:none; border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-answered{ border-top-color:#2a2a2d }
.mj-answered li{ border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-answered li{ border-bottom-color:#2a2a2d }
.mj-answered a{ display:grid; grid-template-columns:auto 1fr auto; gap:var(--sp-4);
  align-items:baseline; padding:11px 0; text-decoration:none }
.mj-answered__k{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400); min-width:66px }
.mj-answered__q{ font-family:var(--font-display); font-weight:700; font-size:.96rem; line-height:1.35;
  color:var(--mj-black) }
body.dark-mode .mj-answered__q{ color:#fff }
.mj-answered a:hover .mj-answered__q{ color:var(--mj-red) }
.mj-answered__a{ font-size:.84rem; color:var(--mj-text-muted); text-align:right; white-space:nowrap }
@media(max-width:560px){ .mj-answered a{ grid-template-columns:1fr }
  .mj-answered__a{ text-align:left } }

/* --- I · BY YOUR BUSINESS — self-selection, NOT profiling ------------------ */
.mj-biz{ display:flex; flex-wrap:wrap; gap:2px; background:var(--mj-rule);
  border:1px solid var(--mj-rule) }
body.dark-mode .mj-biz{ background:#2a2a2d; border-color:#2a2a2d }
.mj-biz__c{ flex:1 1 150px; background:var(--mj-surface); padding:var(--sp-4);
  text-decoration:none; display:block; text-align:center }
body.dark-mode .mj-biz__c{ background:#131315 }
.mj-biz__c:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-biz__c:hover{ background:#1b1b1e }
.mj-biz__i{ display:block; font-size:1.3rem; margin-bottom:7px }
.mj-biz__t{ display:block; font-family:var(--font-display); font-weight:800; font-size:.92rem;
  color:var(--mj-black); margin-bottom:3px }
body.dark-mode .mj-biz__t{ color:#fff }
.mj-biz__c:hover .mj-biz__t{ color:var(--mj-red) }
.mj-biz__n{ font-family:var(--font-meta); font-size:.62rem; color:var(--mj-gray-400) }

/* --- J · VERTICAL PROGRESS — renamed .mj-progress → .mj-vprog, 2026-08-05.
   `.mj-progress` was ALREADY the reading-progress indicator (a 50x50 absolutely
   positioned element). Reusing the name put a stray white square at the top-left
   of every index page — a collision I introduced in Phase 4 and did not catch
   because both elements rendered without erroring. A class name is an API. --  — the honest version of a thin site ------------- */
.mj-vprog{ border:1px solid var(--mj-rule); background:var(--mj-surface); padding:var(--sp-5);
  margin:var(--sp-5) 0 }
body.dark-mode .mj-vprog{ background:#131315; border-color:#2a2a2d }
.mj-vprog__bar{ display:flex; gap:2px; margin:0 0 var(--sp-4) }
.mj-vprog__bar i{ flex:1; height:8px; background:var(--mj-gray-200) }
body.dark-mode .mj-vprog__bar i{ background:#2a2a2d }
.mj-vprog__bar i.live{ background:var(--mj-growth) }
.mj-vprog__bar i.soon{ background:var(--mj-gray-300) }
.mj-vprog__list{ display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr));
  gap:var(--sp-3) var(--sp-5); margin:0; padding:0; list-style:none }
.mj-vprog__list li{ display:flex; align-items:baseline; gap:8px; font-size:.88rem;
  color:var(--mj-text-muted) }
.mj-vprog__list b{ font-family:var(--font-display); font-weight:700; color:var(--mj-black) }
body.dark-mode .mj-vprog__list b{ color:#fff }
.mj-vprog__list .st{ font-family:var(--font-meta); font-size:.58rem; letter-spacing:1px;
  text-transform:uppercase; font-weight:700; padding:1px 6px; border:1px solid var(--mj-rule);
  color:var(--mj-gray-400); margin-left:auto; white-space:nowrap }
body.dark-mode .mj-vprog__list .st{ border-color:#33333a }
.mj-vprog__list .st--live{ border-color:var(--mj-growth); color:#08694c }
body.dark-mode .mj-vprog__list .st--live{ color:#34d8a6; border-color:#1e5c48 }

/* --- Phase 4 dark-mode specificity fixes ----------------------------------
   Two rules were being beaten by their own dark-mode overrides, and in both
   cases the ELEMENT LOST ITS MEANING rather than merely its colour:
     .mj-changed__was  — the superseded verdict rendered identical to the new
                         one, so "we changed our mind" showed two live verdicts
     .mj-vprog .live — live verticals rendered the same grey as unplanned
                         ones, which is the only distinction the bar exists for
   Both are the specificity trap: body.dark-mode .X (0,2,1) beats .Y .X (0,2,0). */
body.dark-mode .mj-changed__was .mj-changed__v{ color:var(--mj-gray-400) }
body.dark-mode .mj-vprog__bar i.live{ background:var(--mj-growth) }
body.dark-mode .mj-vprog__bar i.soon{ background:#4a4a52 }

/* =============================================================================
   ARTICLE SHELL — promoted from page furniture, 2026-08-05 (Phase 5)
   Every prototype re-declared the kicker/H1/dek in its own <style> block because
   each one was a standalone page. The BUILD needs them as real elements, and the
   L0 law does not stop at the prototype boundary: a renderer may only emit
   classes that exist here. So they are promoted rather than re-invented.
   ============================================================================= */
.mj-article{ max-width:var(--article-width); margin:0 auto; padding:var(--sp-7) 0 var(--sp-9) }
/* ON A PHONE THE COLUMN IS THE VIEWPORT, so the reading measure's zero side
   padding put the H1, the dek and every paragraph hard against both screen
   edges. Invisible on a desktop, where the 680px column is centred with room
   around it — and the prototypes hid it too, because each one wraps itself in
   its own `.bg-shell`/`.cx-shell` page furniture that the site does not use.
   Found 2026-08-27 by reading a real page at 375px. */
@media(max-width:760px){
  .mj-article{ padding-left:var(--sp-4); padding-right:var(--sp-4) }
}
.mj-article > p{ font-family:var(--font-body); font-size:1.06rem; line-height:1.78;
  color:var(--mj-ink); margin:0 0 var(--sp-5) }
body.dark-mode .mj-article > p{ color:#e8e8ea }
.mj-article h1{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(1.9rem,4.4vw,2.8rem); line-height:1.05; letter-spacing:-.018em;
  margin:0 0 var(--sp-4); color:var(--mj-black) }
body.dark-mode .mj-article h1{ color:#fff }
/* AN ELEMENT DECLARES ITS OWN TYPOGRAPHY COMPLETELY, or it borrows whatever
   its neighbour happens to set. The kicker declared family and size but not
   LINE-HEIGHT, so it inherited 1.78 from `.mj-article > p` — a prose measure on
   a 10px uppercase label. The ownership probe found it: the winning rule for
   the kicker's line-height was a paragraph rule.
   Partial typography is not a smaller version of complete typography. It is a
   dependency on whatever else is in the room. */
.mj-article > .mj-kicker,
.mj-kicker{ display:inline-block; font-family:var(--font-meta); line-height:1.2; font-size:.66rem;
  letter-spacing:2px; text-transform:uppercase; font-weight:700; color:var(--mj-red);
  margin:0 0 var(--sp-3) }
/* `.mj-article > .mj-kicker` is NOT redundant. `.mj-article > p` (0,1,1) beats a
   bare `.mj-kicker` (0,1,0), so the kicker silently rendered body ink instead of
   brand red. THIRD occurrence of this exact trap in one session — .mj-changed__was
   and .mj-vprog .live were the other two. When a container styles a bare
   element selector, every modifier inside it needs the container in its selector. */
/* The dek was getting its FAMILY from `.mj-article > p` — correct by accident,
   which is the kind of correct that stops being correct when a rule moves. */
.mj-article > .mj-dek,
.mj-dek{ font-family:var(--font-body); font-size:1.14rem; line-height:1.6; color:var(--mj-text-muted);
  margin:0 0 var(--sp-5); max-width:54ch }

/* Logo size modifiers — promoted from inline styles in the chrome prototype,
   2026-08-05 (Phase 6). The header and footer marks are legitimately different
   sizes, but a size is a design decision and design decisions live here.
   Caught by the build's L0 gate the moment the chrome was extracted. */
.mj-logo-svg--sm{ height:22px }
.mj-logo-svg--footer{ height:24px; margin-bottom:4px }
/* THE FOOTER WEARS THE BRAND KIT'S OWN DARK-GROUND LOCKUP.
   It was rendering the ADAPTIVE svg with `.on-dark`, and that class inverts the
   mark: `.on-dark .mj-logo__icon{fill:#fff}` with `.mj-logo__m{fill:#ec2227}`
   turned the red square into a white one with a red M — a lockup the brand kit
   does not contain, on the one surface every page ends on. Lucky: "the version
   of the logo in the footer is incorrect, you should have used this variant:
   …/horizontal/mashjoy.com logo-dark.svg".
   That file is embedded verbatim, fills and all, and carries NO `mj-logo__*`
   classes — so neither the adaptive rules nor dark mode can repaint it. This
   class sizes it and nothing else. */
.mj-logo-lockup{ display:block; width:auto; height:26px }
.mj-logo-lockup--footer{ height:24px; margin-bottom:4px }

/* --- Mega-menu panel: the CLOSED state ------------------------------------
   Promoted from mashjoy-site-chrome.html's own <style> block, 2026-08-05.
   The panel's OPEN state was already in the library (`.mj-nav>ul>li:hover .mm`)
   but its base/closed state was page furniture — so the moment the chrome was
   EXTRACTED for the build, the panel arrived with no way to be shut and
   rendered permanently open over the article.

   An element whose only hiding rule lives in one page's <style> is not an
   element, it is a page. `_audit.py`'s L0 rule missed this because it only
   looks for `.mj-` prefixed selectors, and `.mm` predates the convention. */
.mm{position:absolute;left:0;right:0;top:100%;background:var(--mj-surface);
  border-top:1px solid var(--mj-gray-200);border-bottom:1px solid var(--mj-gray-200);
  box-shadow:0 24px 48px -24px rgba(0,0,0,.18);
  opacity:0;visibility:hidden;transform:translateY(-8px);transition:.18s ease;z-index:120}
body.dark-mode .mm{background:#141416;border-color:#2a2a2a;box-shadow:0 24px 48px -24px rgba(0,0,0,.6)}

/* =============================================================================
   CHROME STATES — the four header_style and four footer_style values, realised
   Design authority, Opus 5, 2026-08-05 (Phase 6, chrome conditionals).

   `smartcms-header.header_style` and `smartcms-footer.footer_style` each define
   four values. The chrome markup is ONE header and ONE footer; the four styles
   are COMPOSITIONS of its parts, selected by a body class. That is the same
   model WordPress uses — one template, conditional display — and it means a
   design change lands in one place instead of four near-copies drifting apart.

   THE RULE UNDERNEATH THE MATRIX (site-chrome-conditional mapping §2):
   the header WIDENS where the reader is exploring and NARROWS where they are
   deciding. Every extra nav item on a converting page is an exit.
   ============================================================================= */

/* ── HEADERS ─────────────────────────────────────────────────────────────── */
/* 🪟 Mega Menu — homepage + vertical archives. The reader has not chosen a
   vertical yet, so the mega menu IS the routing layer: 26 verticals cannot
   live in a horizontal bar. (Default: everything shows.) */

/* 📋 Header with Menu — Explainer · How-To · Business Guide · List.
   Full nav stays (a reader who finishes should find the next thing) but the
   mega panels do NOT open: on an article, a hover that covers the first
   paragraph is a nav that fights the page. */
body.chrome-menu .mj-nav>ul>li:hover .mm{ opacity:0; visibility:hidden }
body.chrome-menu .mj-nav>ul>li>a .chev{ display:none }

/* 📏 Minimal — Product Review · Comparison · FAQ · Deals. CONVERSION
   CONTAINMENT: logo, search, one CTA. */
body.chrome-minimal .mj-util,
body.chrome-list .mj-util{ display:none }
body.chrome-minimal .mj-nav,
body.chrome-list .mj-nav{ display:none }
body.chrome-minimal .mj-burger,
body.chrome-list .mj-burger{ display:none }
body.chrome-minimal .mj-contain,
body.chrome-list .mj-contain{ display:flex }

/* 🎯 With CTA Button — single-purpose pages (offer landings). */
body.chrome-cta .mj-util{ display:none }
body.chrome-cta .mj-nav{ display:none }

/* ── FOOTERS ─────────────────────────────────────────────────────────────── */
/* Parts hidden by default; each style switches on the ones it carries.
   THE ONE RULE THAT IS NOT NEGOTIABLE: .ft-disclosure and .ft-legal render in
   ALL FOUR styles. A disclosure present in three footers out of four is worse
   than none, because the omission looks deliberate. They are never hidden here. */
.ft-ribbon, .ft-top3, .ft-inner{ display:none }

/* 🧱 3-Column — homepage + vertical archive. The full sitemap moment. */
body.foot-cols .ft-inner{ display:block }

/* 📣 Newsletter + Promo CTA — Explainer · How-To · Business Guide · List.
   A reader who finished an explainer is a good subscriber and a poor buyer:
   ask for the email, not the sale. */
body.foot-news .ft-ribbon{ display:block }
body.foot-news .ft-inner{ display:block }

/* 🔻 Mini — Product Review · Comparison · FAQ · Deals. Conversion continuation:
   disclosure, legal, top-3 offers. Nothing that pulls sideways. */
body.foot-mini .ft-top3{ display:block }

/* 📄 Legal Only — privacy, terms, disclosure pages. No marketing on a
   privacy page. (Disclosure + legal only; nothing else switches on.) */

/* ── TOP BAR ─────────────────────────────────────────────────────────────── */
/* The top bar and the Minimal header must NEVER appear together:
   if you have decided to contain, contain.

   THE BAR IS DRIVEN BY `topbar-on`, WHICH IS THE CLASS THAT DECIDES IT.
   It was driven by `.mj-topbar` — a class that exists in no prototype and no
   template, so the rule matched nothing and had never once had an effect. The
   two header styles it was aiming at hide the bar through their own rules
   above, which is why nobody noticed; the surface it actually left broken was
   the LEGAL page, which keeps the full menu (a reader on a privacy page should
   be able to leave it) but is listed in TOPBAR_OFF. chrome.py had decided the
   bar was off and the stylesheet had no way to hear it.

   Driving it off `topbar-on` also makes the resolver's decision legible in the
   markup instead of inferred from two header classes. */
body:not(.topbar-on) .mj-util{ display:none }

/* Product-box body column + the comparison table's scroll minimum.
   Both promoted 2026-08-05 when the build's L0 gate caught them: `.mj-pb__body`
   was used in the ranked product box and styled nowhere, and the table carried
   `style="min-width:620px"` inline. A table needs a minimum width to stay
   readable while it scrolls sideways on a phone — that is a design decision, so
   it belongs here and not in markup. */
.mj-pb__body{ min-width:0 }
.mj-tw__scroll table{ min-width:620px }

/* The rotation note carries extra top space when it closes a panel rather than
   a rotating band. Promoted from an inline style on the homepage's vertical
   progress panel, 2026-08-05 — caught by the build's L0 gate. */
.mj-progress .mj-rot__note{ margin-top:var(--sp-4) }

/* =============================================================================
   INDEX SHELL + SEARCH OVERLAY + MEGA KEYBOARD STATE
   Design authority, Opus 5, 2026-08-05.
   ============================================================================= */

/* ── THE INDEX SHELL ──────────────────────────────────────────────────────
   Promoted from `.hp-shell`, which was page furniture in three prototypes.
   AN INDEX IS NOT AN ARTICLE. The 680px measure exists so PROSE is readable;
   a homepage is a mosaic, a grid and a router, and squeezing those into the
   reading measure was the bug on the built homepage — it rendered at article
   width on a desktop and wasted two thirds of the screen. */
.mj-shell{ max-width:var(--chrome-max); margin:0 auto;
  padding:var(--sp-6) var(--sp-5) var(--sp-9) }
@media(max-width:560px){ .mj-shell{ padding:var(--sp-5) var(--sp-4) var(--sp-8) } }

/* ── MEGA MENU: the keyboard/touch path CSS hover cannot provide ────────── */
.mj-nav>ul>li.is-open .mm{ opacity:1; visibility:visible; transform:translateY(0) }
.mj-nav>ul>li.is-open>a{ color:var(--mj-red); border-bottom-color:var(--mj-red) }
/* CONTAINMENT BEATS THE CLICK PATH. `chrome-menu` suppresses the mega panels on
   hover; without this they would still open on a click or a keypress, so the
   containment rule would hold for a mouse and fail for a keyboard — the worst
   kind of rule, one that is true only for the readers you happened to test. */
body.chrome-menu .mj-nav>ul>li.is-open .mm,

/* ── THE RANKING BAR · `body.chrome-list` ─────────────────────────────────
   A list article contains like a comparison — it is a deciding page — but its
   header carries the RANKING rather than "you are reading", because the reader
   is picking among many candidates and the ranking is the navigation the page
   already owns. Same shell, different cargo. */
body.chrome-list .cc-lbl{ color:var(--mj-red) }
body.chrome-list .cc-now{ font-family:var(--font-display); font-weight:700 }

body.chrome-minimal .mj-nav>ul>li.is-open .mm{ opacity:0; visibility:hidden }

/* ── SEARCH OVERLAY ────────────────────────────────────────────────────── */
.mj-search{ position:fixed; inset:0; z-index:10000; display:none;
  background:rgba(10,10,10,.55); backdrop-filter:blur(3px) }
.mj-search.is-open{ display:block }
body.search-open{ overflow:hidden }
.mj-search__panel{ max-width:var(--wide); margin:8vh auto 0; background:var(--mj-surface);
  border:1px solid var(--mj-rule); box-shadow:0 30px 80px -30px rgba(0,0,0,.5) }
body.dark-mode .mj-search__panel{ background:#141417; border-color:#2a2a2d }
.mj-search__bar{ display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-search__bar{ border-bottom-color:#2a2a2d }
.mj-search__bar input{ flex:1; font-family:var(--font-display); font-weight:700;
  font-size:1.2rem; border:0; background:transparent; color:var(--mj-black); outline:none }
body.dark-mode .mj-search__bar input{ color:#fff }
.mj-search__bar input::placeholder{ color:var(--mj-gray-400); font-weight:400 }
.mj-search__x{ font-family:var(--font-meta); font-size:.66rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400); cursor:pointer;
  background:transparent; border:1px solid var(--mj-rule); padding:5px 10px }
body.dark-mode .mj-search__x{ border-color:#33333a }
.mj-search__results{ max-height:60vh; overflow-y:auto }
.mj-search__hit{ display:grid; grid-template-columns:88px 1fr; gap:var(--sp-4);
  align-items:baseline; padding:var(--sp-3) var(--sp-5);
  border-bottom:1px solid var(--mj-rule); text-decoration:none }
body.dark-mode .mj-search__hit{ border-bottom-color:#2a2a2d }
.mj-search__hit:hover{ background:var(--mj-surface-2) }
body.dark-mode .mj-search__hit:hover{ background:#1b1b1e }
.mj-search__kind{ font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.2px;
  text-transform:uppercase; font-weight:700; color:var(--mj-gray-400) }
.mj-search__t{ font-family:var(--font-display); font-weight:800; font-size:1rem;
  color:var(--mj-black) }
body.dark-mode .mj-search__t{ color:#fff }
.mj-search__hit:hover .mj-search__t{ color:var(--mj-red) }
.mj-search__d{ grid-column:2; font-size:.86rem; line-height:1.55; color:var(--mj-text-muted) }
/* THE EMPTY STATE IS THE DESIGN PROBLEM, not a message. "No results" is a dead
   end; this says what we DO cover, admits what we do not yet, and offers a way
   to ask. An honest "not yet, and here is what is" keeps a reader. */
.mj-search__empty{ padding:var(--sp-5) }
.mj-search__note{ font-family:var(--font-meta); font-size:.8rem; line-height:1.7;
  color:var(--mj-text-muted); margin:0 0 var(--sp-2) }
.mj-search__note b{ color:var(--mj-black); font-weight:700 }
body.dark-mode .mj-search__note b{ color:#fff }

/* Visually hidden — present for screen readers and SEO, absent for the eye.
   An index needs an H1 (search and assistive tech both require one) and a reader
   needs the lead story in that space. This is the correct resolution of two real
   requirements that want different things, not a hack. Directive v3 Part 8.3. */
.mj-vh{ position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0 }

/* Archive head — a topic name IS content ("Card machines in South Africa" tells
   the reader where they are and matches the query they arrived on), unlike a
   site name, which is not. So an archive keeps a visible H1 where the homepage
   hides one. Directive v3 Part 8.3. */
.mj-archive-head{ padding:var(--sp-5) 0 var(--sp-4); border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-archive-head{ border-bottom-color:#2a2a2d }
.mj-archive-head h1{ font-family:var(--font-display); font-weight:800;
  font-size:clamp(1.9rem,4vw,2.7rem); line-height:1.05; letter-spacing:-.018em;
  margin:0 0 var(--sp-3); color:var(--mj-black); max-width:24ch }
body.dark-mode .mj-archive-head h1{ color:#fff }
.mj-archive-head .mj-dek{ max-width:62ch }

/* Entity-hero logo placeholder. Promoted from an inline style on the brand hub,
   2026-08-05. The BRAND ACCENT beside it stays inline and correctly so — a CSS
   custom property carrying `brand_brand_colors` is per-entity DATA, not a
   design decision, and it is what lets Yoco's hub read as Yoco's without us
   imitating them (entity-archives §1.1). */
.mj-ent-hero__logo{ display:flex; align-items:center; justify-content:center;
  font-size:.5rem; text-align:center; line-height:1.3 }

/* Score denominator and the centred tracked-row. Both promoted from inline
   declarations in the entity archives, 2026-08-05 — caught by the L0 gate.
   The "/ 5" beside a score is a typographic decision (it must recede so the
   number carries), not a per-instance value, so it belongs here. */
.mj-ent-hero__of{ font-size:.55em; color:var(--mj-gray-400) }
.mj-av-tracked--centre{ justify-content:center }

/* =============================================================================
   CHROME INTERNALS — promoted from mashjoy-site-chrome.html's <style>, 2026-08-05

   The mega menu's columns and rail, and the footer's brand block, link columns,
   social row, top-3 grid and newsletter ribbon: the layout of two of the most-seen
   elements on the site, all of it living in ONE PROTOTYPE'S PAGE FURNITURE.

   So the build extracted the chrome's MARKUP and got none of its LAYOUT — the mega
   menu opened as a stack of default-blue links bleeding to the left edge.

   Same defect as `.mm` and `.mj-contain`, at scale:
       AN ELEMENT WHOSE LAYOUT LIVES IN ONE PAGE IS NOT AN ELEMENT, IT IS A PAGE.
   ============================================================================= */
.mm-col h4{font-family:var(--font-meta);font-size:.68rem;letter-spacing:2px;text-transform:uppercase;
  color:var(--mj-gray-400);margin:0 0 var(--sp-4);padding-bottom:var(--sp-2);
  border-bottom:1px solid var(--mj-gray-200)}
body.dark-mode .mm-col h4{border-color:#2a2a2a}
.mm-col ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--sp-3)}
.mm-col a{font-family:var(--font-body);font-size:1rem;color:var(--mj-charcoal);text-decoration:none;
  display:inline-flex;align-items:center;gap:8px;transition:.15s}
body.dark-mode .mm-col a{color:#cfcfd2}
.mm-col a:hover{color:var(--mj-red);transform:translateX(3px)}
.mm-col a .tag{font-family:var(--font-meta);font-size:.6rem;letter-spacing:1px;text-transform:uppercase;
  color:var(--mj-growth-text);border:1px solid var(--mj-growth);border-radius:999px;padding:1px 7px}
body.dark-mode .mm-rail{border-color:#2a2a2a}
/* ── THE MEGA MENU, REWORKED (Lucky, 2026-09-16) ──────────────────────────
   "The design isn't that nice or that interesting, and doesn't suit the overall
   trust element of the website. It leans more towards the AI side of the designs,
   which we are trying to avoid. And I don't like the highlights or the
   backgrounds where you have the greys because the grey is a little bit dull.
   Keep it all white and make it more interesting in other ways."

   The two decorative gradient blocks — a navy→red rectangle standing in for a
   photograph, and a blue→green square beside every mini — were the whole of the
   "AI" reading: colour where there is no information, in the one panel that is
   supposed to say this desk knows its subject. An editorial menu earns interest
   from TYPE and from FACTS: a dated claim, a headline at real size, and the kind
   of piece each link is. Nothing here is a placeholder for an image we do not
   have. */
.mm-feat{display:block;text-decoration:none}
.mm-feat__chip{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-meta);
  font-size:.58rem;letter-spacing:1.4px;text-transform:uppercase;color:var(--mj-red);
  margin-bottom:var(--sp-2)}
.mm-feat__chip::before{content:"";width:6px;height:6px;flex:none;background:var(--mj-red)}
.mm-feat h3{font-family:var(--font-display);font-weight:800;font-size:1.34rem;line-height:1.2;
  margin:0 0 6px;color:var(--mj-black);text-wrap:balance}
body.dark-mode .mm-feat h3{color:#fff}
.mm-feat:hover h3{color:var(--mj-red)}
.mm-feat p{margin:0;font-size:.85rem;color:var(--mj-gray-400)}
.mm-mini{display:flex;flex-direction:column;gap:3px;text-decoration:none;
  padding-top:var(--sp-3);margin-top:var(--sp-3);border-top:1px solid var(--mj-rule)}
body.dark-mode .mm-mini{border-color:#2a2a2a}
/* The arrow appears on approach rather than sitting there — movement is the
   interest, not another block of colour. */
.mm-mini h5::after{content:" \2192";color:var(--mj-red);opacity:0;transition:.16s}
.mm-mini:hover h5::after{opacity:1}
.mm-mini h5{margin:0;font-family:var(--font-display);font-weight:600;font-size:.92rem;line-height:1.3;
  color:var(--mj-black);text-transform:none;letter-spacing:0}
body.dark-mode .mm-mini h5{color:#f2f2f2}
.mm-mini:hover h5{color:var(--mj-red)}
.mm-mini span{font-family:var(--font-meta);font-size:.62rem;letter-spacing:1px;text-transform:uppercase;color:var(--mj-gray-400)}
body.foot-continuation .ft-ribbon,body.foot-educational .ft-ribbon{display:block}
.ft-ribbon-in{max-width:var(--chrome-max);margin:0 auto;padding:var(--sp-6) var(--sp-5);
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-5);flex-wrap:wrap}
.ft-ribbon-in h3{font-family:var(--font-display);font-weight:700;font-size:1.5rem;margin:0;color:#fff;text-wrap:balance}
.ft-ribbon-in p{margin:6px 0 0;color:#b6b6bb;font-size:.9rem}
.ft-ribbon-in .rib-cta{background:var(--mj-red);color:#fff;text-decoration:none;font-family:var(--font-display);
  font-weight:600;text-transform:uppercase;letter-spacing:.4px;padding:13px 26px;border-radius:var(--radius-sm);white-space:nowrap}
.ft-ribbon-in .rib-cta:hover{background:var(--mj-red-hover,#BA1F33)}
body.foot-continuation .ft-top3{display:block}
.ft-top3-in{max-width:var(--chrome-max);margin:0 auto;padding:var(--sp-7) var(--sp-5)}
.ft-top3-in>.eyebrow{font-family:var(--font-meta);font-size:.68rem;letter-spacing:2px;text-transform:uppercase;color:var(--mj-red);margin:0 0 var(--sp-4)}
.ft-top3-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-4)}
.ft-prod .rank{font-family:var(--font-meta);font-size:.62rem;letter-spacing:1.5px;text-transform:uppercase;color:var(--mj-growth-text)}
.ft-prod h4{font-family:var(--font-display);font-weight:700;font-size:1.15rem;margin:0;color:#fff}
.ft-prod p{margin:0;font-size:.85rem;color:#a9a9ae}
.ft-prod .go{margin-top:6px;font-family:var(--font-display);font-weight:600;font-size:.78rem;text-transform:uppercase;
  letter-spacing:.4px;color:#fff;background:var(--mj-red);padding:8px 14px;border-radius:var(--radius-sm);align-self:flex-start}

/* continuation state collapses the broad sitemap to just the pillar column */
body.foot-continuation .ft-col-cat,
body.foot-continuation .ft-col-company{display:none}
body.foot-continuation .ft-cols{grid-template-columns:1.6fr 1fr}
.ft-brand .wm b{color:var(--mj-red)}
.ft-brand p{color:#a9a9ae;font-size:.92rem;line-height:1.65;margin:0 0 var(--sp-4);max-width:42ch}
.ft-social{display:flex;gap:var(--sp-3)}
body.foot-continuation .ft-social{display:none}
.ft-social a:hover{border-color:var(--mj-red);color:#fff;background:var(--mj-red)}
.ft-social svg{width:18px;height:18px;fill:currentColor}
.ft-col h4{font-family:var(--font-meta);font-size:.62rem;letter-spacing:2px;text-transform:uppercase;color:#6f6f76;margin:0 0 var(--sp-4)}
.ft-col ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--sp-3)}
/* OBSERVATION 20.6 — the footer's NAVIGATION takes the display face; its prose
   does not. Lucky: "you can add a substitute font, but always also try to make
   it smaller or about the same size … keep the affiliate disclosure element the
   way it appears, and the body font can still be used where appropriate."

   The split follows the same rule as the rest of the site: a thing you PRESS
   wears `--font-display`, a thing you READ wears `--font-body`. So the column
   links change and the disclosure, the newsletter copy and the legal line do
   not — they are sentences. Same size band, slightly tighter tracking: a change
   of voice, not of loudness. */
.ft-col a{ font-family:var(--font-display); font-weight:500; font-size:.78rem;
  letter-spacing:.2px; color:#cfcfd2; text-decoration:none;
  transition:color .15s ease }
.ft-col a:hover{color:var(--mj-red)}
.ft-disclosure .box{margin-top:var(--sp-6);padding:var(--sp-4) var(--sp-5);border:1px solid #262629;border-radius:var(--radius-md);
  background:#0f0f11;color:#9a9aa0;font-size:.8rem;line-height:1.6;display:flex;gap:var(--sp-3);align-items:flex-start}
.ft-disclosure .box svg{width:18px;height:18px;stroke:var(--mj-growth);stroke-width:1.8;fill:none;flex:none;margin-top:2px}
.ft-disclosure .box b{color:#d7d7db}
.ft-legal .copy{font-family:var(--font-meta);font-size:.72rem;letter-spacing:1px;text-transform:uppercase;color:#6f6f76}
.ft-legal .links{display:flex;gap:var(--sp-5)}
.ft-legal .links a{color:#9a9aa0;text-decoration:none;font-size:.8rem}
.ft-legal .links a:hover{color:var(--mj-red)}
@media(max-width:1200px){
.mm-inner{grid-template-columns:1fr 300px;gap:var(--sp-6)}
}
@media(max-width:1200px){
.mm-rail{padding-left:var(--sp-5)}
}
@media(max-width:760px){
.ft-top3-grid{grid-template-columns:1fr}
}
@media(max-width:640px){
body.foot-continuation .ft-cols{grid-template-columns:1fr}
}

/* Second pass, same day: the first anchored its selector test on (^|,) and
   therefore skipped every rule preceded by a comment — including .mm-inner and
   .mm-cols, the two rules that lay the mega menu out AT ALL. The panel opened as
   one stacked column. A promotion that moves most of an element's CSS leaves it
   more broken than moving none. */

.mm-inner{max-width:var(--chrome-max);margin:0 auto;padding:var(--sp-7) var(--sp-5);
  display:grid;grid-template-columns:1fr 360px;gap:var(--sp-8)}
.mm-cols{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-6) var(--sp-7);align-content:start}
.mm-rail{border-left:1px solid var(--mj-gray-200);padding-left:var(--sp-7);
  display:flex;flex-direction:column;gap:var(--sp-5)}
.mm-rail-h{font-family:var(--font-meta);font-size:.68rem;letter-spacing:2px;text-transform:uppercase;
  color:var(--mj-red);margin:0}
.ft-inner{max-width:var(--chrome-max);margin:0 auto;padding:var(--sp-9) var(--sp-5) var(--sp-6)}
.ft-ribbon{display:none;background:linear-gradient(135deg,#1A2332,#0A0A0A);border-bottom:1px solid #2a2a2a}
.ft-top3{display:none;border-bottom:1px solid #222}
.ft-prod{display:flex;flex-direction:column;gap:8px;background:#141416;border:1px solid #262629;
  border-radius:var(--radius-md);padding:var(--sp-5);text-decoration:none}
.ft-cols{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:var(--sp-7)}
/* THE FOOTER'S MOBILE LAYOUT HAD NEVER WORKED (found 2026-08-27).
   Its responsive rules live ~25 lines ABOVE this declaration, so at equal
   specificity this one won and the footer stayed four columns at every width.
   A 4-column grid needing 534px inside a 327px phone column is what made EVERY
   page on the site scroll horizontally — the one thing the responsive rule
   forbids outright, on the device most of our readers use. Re-declared here,
   after the base, where source order cannot silently undo them. */
@media(max-width:900px){ .ft-cols{grid-template-columns:1fr 1fr} }
@media(max-width:520px){ .ft-cols{grid-template-columns:1fr} }
.ft-brand .wm{font-family:var(--font-display);font-weight:700;font-size:1.7rem;color:#fff;margin:0 0 var(--sp-3)}
.ft-social a{width:38px;height:38px;border:1px solid #333;border-radius:var(--radius-sm);
  display:flex;align-items:center;justify-content:center;color:#cfcfd2;transition:.15s}
.ft-disclosure{max-width:var(--chrome-max);margin:0 auto;padding:0 var(--sp-5)}
.ft-legal{max-width:var(--chrome-max);margin:0 auto;padding:var(--sp-6) var(--sp-5) 0;
  display:flex;justify-content:space-between;align-items:center;gap:var(--sp-4);flex-wrap:wrap;
  border-top:1px solid #222;margin-top:var(--sp-7)}

/* `.thumb` and `.m-thumb` are <span>s carrying `width` and `aspect-ratio`.
   AN INLINE ELEMENT IGNORES BOTH, so the box collapsed to 0x0 and the
   absolutely-positioned 'Updated July 2026' chip inside it — anchored to
   bottom:12px of nothing — floated up and overlapped the Editor's Pick
   heading above. Present in the prototype since it was written; it only
   surfaced when the mega menu was finally opened on a built page.
   Declared here rather than changing the markup: the markup is correct,
   the CSS was incomplete. */

/* ── THE MOBILE DRAWER ─────────────────────────────────────────────────────
   Promoted from mashjoy-site-chrome.html's <style>, 2026-08-14. The THIRD
   element to be found this way, after `.mm` and the chrome's layout, and the
   most expensive of the three: BELOW 900px THE DRAWER IS THE ENTIRE
   NAVIGATION — the mega menu is hidden and the burger is the only route off
   the page. Its markup had never been extracted at all, so the built site
   shipped a burger button that opened nothing, and these 25 rules would have
   been missing even if it had.
   ------------------------------------------------------------------------ */
.md-top{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-4) var(--sp-5);
  border-bottom:1px solid var(--mj-gray-200)}
body.dark-mode .md-top{border-color:#2a2a2a}
.md-top .wm{font-family:var(--font-display);font-weight:700;font-size:1.25rem}
.md-top .wm b{color:var(--mj-red)}
.md-close{background:none;border:none;cursor:pointer;color:inherit;padding:4px}
.md-close svg{width:24px;height:24px;stroke:currentColor;stroke-width:2;fill:none}
.md-search{padding:var(--sp-4) var(--sp-5)}
.md-search input{width:100%;padding:11px 14px;border:1px solid var(--mj-gray-300);border-radius:var(--radius-sm);
  font-family:var(--font-body);font-size:.95rem;background:transparent;color:inherit}
.md-scroll{flex:1;overflow-y:auto;padding:0 var(--sp-5) var(--sp-5)}
.md-acc{border-bottom:1px solid var(--mj-gray-200)}
body.dark-mode .md-acc{border-color:#2a2a2a}
.md-acc>button{width:100%;background:none;border:none;cursor:pointer;color:inherit;
  display:flex;justify-content:space-between;align-items:center;padding:15px 0;
  font-family:var(--font-display);font-weight:600;font-size:1rem;text-transform:uppercase;letter-spacing:.4px}
.md-acc>button svg{width:16px;height:16px;stroke:currentColor;stroke-width:2.5;fill:none;transition:transform .2s}
.md-acc.open>button svg{transform:rotate(180deg)}
.md-panel{max-height:0;overflow:hidden;transition:max-height .28s ease}
.md-acc.open .md-panel{max-height:600px}
.md-panel ul{list-style:none;margin:0;padding:0 0 12px}
.md-panel a{display:block;padding:9px 0 9px 4px;color:var(--mj-charcoal);text-decoration:none;font-size:.98rem}
body.dark-mode .md-panel a{color:#cfcfd2}
.md-panel a:hover{color:var(--mj-red)}
.md-foot{padding:var(--sp-5);border-top:1px solid var(--mj-gray-200);display:flex;flex-direction:column;gap:var(--sp-3)}
body.dark-mode .md-foot{border-color:#2a2a2a}
.md-foot .md-sub{background:var(--mj-red);color:#fff;text-align:center;text-decoration:none;padding:12px;
  border-radius:var(--radius-sm);font-family:var(--font-display);font-weight:600;text-transform:uppercase;letter-spacing:.4px}
.md-foot .md-theme{display:flex;align-items:center;justify-content:space-between;
  font-family:var(--font-meta);font-size:.7rem;letter-spacing:1.5px;text-transform:uppercase;color:var(--mj-gray-400)}
.md-foot .md-theme button{background:none;border:1px solid var(--mj-gray-300);color:inherit;border-radius:var(--radius-sm);
  padding:5px 12px;cursor:pointer;font-family:var(--font-meta);font-size:.68rem;text-transform:uppercase}

/* ── THE CHROME'S BREAKPOINT ───────────────────────────────────────────────
   Written 2026-08-14, and it had never existed.

   The chrome prototype shipped a mega menu, a burger button AND a full mobile
   drawer, and nothing that ever switched between them. Confirmed against the
   original file, not assumed: there was exactly one @media rule in it and it
   governed the containment bar. So at 375px the site rendered five desktop nav
   items across a phone and kept the burger hidden — the drawer was decorative
   from the day it was drawn.

   The header WIDENS where the reader is exploring and NARROWS where they are
   deciding. A phone is not a decision to contain; it is a screen 375px wide.
   ------------------------------------------------------------------------ */
/* THE BAR FITS BETWEEN 901 AND 1140px (fixed 2026-09-17).
   The nav shows from 901px, but at full spacing the bar needs ~1,083px — logo 187,
   six nav items at 32px gaps, then search, theme and Subscribe — so between 901
   and ~1,083px the Subscribe button hung off the right edge (41px at 1017) and
   every page scrolled sideways. The band tightens the SPACING, not the content:
   nav gaps scale with the viewport, the logo drops to its small size, and the
   button keeps its label. Nothing is hidden. */
@media(min-width:901px) and (max-width:1140px){
  .mj-bar{ gap:var(--sp-3); padding:0 var(--sp-4) }
  .mj-nav>ul{ gap:clamp(10px, calc(3.2vw - 18px), 32px) }
  .mj-right{ gap:0 }
  .mj-subscribe{ margin-left:var(--sp-2); padding:8px 12px }
  .mj-bar .mj-logo-svg{ height:22px }
}
@media(max-width:900px){
  :root{ --mj-header-top:65px }      /* the pinned bar is shorter here */
  .mj-nav{ display:none }
  .mj-burger{ display:block }
  .mj-bar{ height:64px; gap:var(--sp-3) }
  /* Containment already hides the burger — a contained page has no drawer to
     open, and offering one would undo the containment on the smallest screen. */
  body.chrome-minimal .mj-burger, body.chrome-cta .mj-burger{ display:none }
}
@media(max-width:760px){
  :root{ --mj-util-h:0px }
  .mj-util{ display:none }
  /* THE PULL-UP MUST GO WITH THE BAR IT PULLS. The header pins by its own top
     edge, lifted by the utility bar's height; with no utility bar that lift
     would drag the nav bar off the top of the screen instead — the same defect
     as the 38px gap, inverted, and invisible on a desktop. */
  .mj-header{ top:0 }
  /* The subscribe CTA lives in the drawer's footer at this width. Two calls to
     subscribe on a 375px header is one more than the logo has room for. */
  .mj-subscribe{ display:none }
}

/* The footer's link-column group. A NAME, NOT A BOX: `display:contents` makes
   the wrapper vanish from layout so the three columns remain direct grid items
   of .ft-cols. It exists so the build can replace all three at once — before it,
   the Categories column was slotted and Top Guides and Company were still the
   prototype's own hard-coded lists, which is a half-fixed footer and worse than
   an unfixed one, because it looks done. */
.ft-linkcols{ display:contents }

/* ── CTA BLOCK — the responsive rules ───────────────────────────────────────
   Promoted from mashjoy-cta-block.html's <style>, 2026-08-14. The base rules for
   .scms-cta were already in the library; only the breakpoints were not, so the
   element collapsed correctly on a desktop and stacked wrongly on a phone —
   the half-promoted state, which is the hardest kind to notice.
   ------------------------------------------------------------------------ */
@media(max-width:560px){.scms-cta--inline .act{width:100%}.scms-cta--inline .act .cta-btn{width:100%}}
@media(max-width:600px){.scms-cta--banner{padding:var(--sp-6) var(--sp-5)}.scms-cta--banner .act{width:100%}.scms-cta--banner .act .cta-btn{width:100%}}
@media(max-width:560px){.scms-cta--midfunnel .routes{grid-template-columns:1fr}}

/* ── THE INSERT-PENDING STATE ───────────────────────────────────────────────
   Written 2026-08-14. The same visual language as `.mj-img__pending`, applied
   to an ELEMENT rather than an image.

   An article can be written, gated and APPROVED while the data for one of its
   inserts is still being sourced. The two obvious responses are both worse than
   this one: dropping the insert makes a "7 Best" article show five and nobody
   counts, and filling it from the surrounding prose invents a number nobody
   verified — on the exact element the truth layer exists to protect.

       A price read out of a sentence is a price nobody verified.

   So it renders as unmistakable scaffolding: the element it stands in for, what
   it is about, and why it is not here yet.
   ------------------------------------------------------------------------ */
.mj-insert-pending{ display:flex; flex-direction:column; align-items:flex-start;
  gap:6px; margin:var(--sp-6) 0; padding:var(--sp-5);
  border:1px dashed var(--mj-gray-300); border-radius:var(--radius-md);
  background:repeating-linear-gradient(45deg,var(--mj-surface-2),var(--mj-surface-2) 12px,
    var(--mj-surface) 12px,var(--mj-surface) 24px);
  font-family:var(--font-meta); font-size:.62rem; letter-spacing:1.4px;
  text-transform:uppercase; color:var(--mj-gray-400) }
.mj-insert-pending__k{ color:var(--mj-red); font-weight:700 }
.mj-insert-pending b{ font-family:var(--font-display); font-size:.95rem; letter-spacing:0;
  text-transform:none; color:var(--mj-ink) }
body.dark-mode .mj-insert-pending{ border-color:#3a3a40 }

/* The comparison table's row label and value cells. ADDRESSING CLASSES, not new
   design: `.mj-tw td` already styles them and these inherit it unchanged. They
   exist because a row label and a value cell are different things doing
   different jobs — only the value cells repeat — and addressing them by index
   across a prototype holding four tables was never going to hold. */
.mj-tw__rowlabel{ }
.mj-tw__cell{ }

/* The review block's score bars read their width from `--w`, which the renderer
   sets from the score itself. It was an inline `width:94%` in the prototype —
   and an inline declaration is a fork of the library, which is why the L0 gate
   rejects them. But a BAR'S WIDTH IS THE SCORE: it is per-item data, the same
   category as a brand's accent colour, and the gate already allows a custom
   property for exactly that reason. The bar can now never disagree with the
   number printed beside it, because both come from one value. */
.scms-rb .rb-f-bar .fill{ width:var(--w,0%) }

/* =============================================================================
   THE BREAK-OUT — inserts are WIDER than the reading measure
   Design authority, Opus 5, 2026-08-15.

   THE FOURTH ELEMENT-LEVEL RULE FOUND TRAPPED AS PAGE FURNITURE, after `.mm`,
   the chrome's layout and the mobile drawer — and the most consequential,
   because it governs EVERY insert on EVERY article.

   Five prototypes each defined their own copy of the same rule, byte for byte,
   under a furniture-prefixed name: `.cx-wide`, `.ex-wide`, `.bg-wide`,
   `.pv-wide`, `.tb-wide`. Furniture is discarded at extraction, so the site
   inherited NONE of them and every table, requirements panel and visual block
   rendered clamped to the 680px reading measure. Lucky, exactly:

       "the tables are being constrained to the width of the text, whereas in
        the prototypes these tables are a little bit wider so that they are easy
        to view and interact with"

   THE MEASURE IS FOR PROSE. A 680px column exists so a sentence is comfortable
   to read. A comparison table is not a sentence — it is a grid the reader scans
   sideways, and squeezing it into the prose measure makes the one element that
   exists to be compared the hardest thing on the page to compare.

   SO THE ELEMENT CARRIES ITS OWN WIDTH. Not a wrapper the writer remembers, not
   a class per article: if a comparison table is always wide, `.mj-tw` is always
   wide, everywhere, forever. That is what makes this copy-and-paste rather than
   a decision to re-make each time.
   ============================================================================= */

/* The break-out itself. Centre on the column, then grow past it — capped at the
   wide measure, and never wider than the viewport can hold. */
/* ── WHAT BREAKS OUT, AND WHAT DOES NOT · owner ruling, 2026-08-21 ───────
       "all the inserts that are info-based or like the body inserts … you must
        make them be in the same width as the site content so that there is no
        too much branching out or overflows, because that kills the balance of
        the readability. Tables will always remain wide … unless it is a tool
        where someone has to interact with it."                        — Lucky

   THE TEST IS WHAT THE READER DOES WITH IT, not how important it is.

       A TABLE is scanned sideways — squeezing a grid into the prose measure
       makes the one element built for comparison the hardest to compare.
       A TOOL is operated — a slider needs room for its own scale.
       EVERYTHING ELSE IS READ, and reading has one comfortable measure. An
       info block set wider than the prose around it does not become clearer;
       it becomes a second column the eye has to re-aim at, mid-article.

   THIS REVERSES MY OWN EARLIER CALL ON `.mj-rec`. I argued a segmented verdict
   is a decision grid and earned the extra width. Lucky named it directly —
   "I would have wanted that which should you pick element to be in the same
   content width" — and reading it on the page he is right: two 320px columns
   read fine, and the jump out of the measure cost more than the columns gained.
   The same goes for the settlement strip and the pair pick, both of which I
   widened this week.

   AND THE TABLE IS NO LONGER WIDE BY TYPE (Lucky, c-9xek + c-auve, 2026-09-12):
   "if the content can fit and be easy to read at the same width as the content,
   keep it like that; if it reads constrained, apply the wider width." A table
   of a label and three columns reads comfortably at 680px and jumped the measure
   anyway, because `.mj-article .mj-tw` broke out on its own. It is taken off
   this list: a table breaks out only when `r_comparison_table` measures that its
   columns need more than the reading measure and adds `.mj-wide`. */
.mj-article .mj-tbl-cap,
.mj-article .mj-img--full,
/* `.mj-wide` IS THE OPT-IN, and it must work in a prototype too — the prototypes
   lay out their column as `.cx-prose`, not `.mj-article`, so a rule scoped to
   the site's wrapper would break out on the site and not in the specification.
   A prototype that does not show what the site shows has stopped being one. */
.mj-wide,
.mj-article .mj-wide{
  width:min(var(--wide), calc(100vw - (2 * var(--sp-5))));
  max-width:none;
  margin-left:50%;
  transform:translateX(-50%);
}

/* A BREAK-OUT INSIDE A CONTAINED HOST IS A BUG (2026-09-05, Lucky's screenshot).
   `.mj-wide` centres on its CONTAINING BLOCK. Put a comparison_table inside a
   short-answer panel and it centres on the strip's padded box instead of the
   article column, so the table escapes both sides, the strip grows taller than
   its content, and the red `.mj-vstrip` rail runs down past the table and
   collides with the caption. That is exactly what shipped on the SARS article.

   The elements are right to be wide in the body; they are wrong to be wide
   inside a panel, and the panel is the thing that knows. Cancelled here rather
   than in the templates, which are generated from the prototypes and must not be
   hand-edited. */
.mj-vstrip__panel .mj-wide,
.mj-vstrip__panel .mj-article .mj-wide{
  width:100%; max-width:100%; margin-left:0; transform:none;
}
/* The tables carry their own horizontal scroll, so a narrow panel is safe. */
.mj-vstrip__panel .mj-tw__scroll{ overflow-x:auto }

/* On a phone the column IS the viewport, so there is nothing to break out of —
   and the transform would push the element off-screen. */
/* 767px, NOT 1024px (Lucky, c-9xek + c-auve, 2026-09-12). The breakouts were switched
   off at every width up to 1024px on the assumption that the column fills the screen
   there. It does not: at 1017px the article is 680px with 337px spare, and the Studio
   preview is narrower still, so every wide element rendered at 680px and a 4-company
   table scrolled sideways. The wide width already clamps to 100vw minus the gutters,
   so it can only be switched off where the column really is the viewport. */
@media(max-width:767px){
  .mj-article .mj-rec,
  .mj-article .mj-req,
  .mj-article .mj-vb,
  .mj-article .mj-ict,
  .mj-article .mj-img--full,
  .mj-article .scms-rb--full,
  .mj-article .mj-tbl-cap,
  .mj-wide,
  .mj-article .mj-wide{ width:100%; margin-left:0; transform:none }
}

/* ── REVIEW BLOCK — the rest of it ──────────────────────────────────────────
   Promoted 2026-08-15. THE FIFTH ELEMENT FOUND TRAPPED AS PAGE FURNITURE,
   and the one Lucky described as "the text styles are not fully applied".
   He was reading it exactly right:
     · `.rb-f-bar` laid out as a 3-track grid here, `display:block` on the site,
       so the score bars rendered as stacked plain text instead of
       label | track | value
     · `.scms-rb-heading` set in Technor here, body font on the site
     · the pros/cons +/− marks, the fit cells, the verdict quote and the facts
       row all styled here and nowhere else
   Twenty-five rules. The element was extracted, bound to 26 slots and gated
   green, and still could not look like itself.
   ------------------------------------------------------------------------ */
.rb-f-bar{display:grid;grid-template-columns:130px 1fr 42px;align-items:center; gap:var(--sp-4);margin-bottom:var(--sp-4)}
.rb-f-bar:last-child{margin-bottom:0}
.rb-f-bar .lbl{font-family:var(--font-meta);font-size:.74rem;letter-spacing:.5px; text-transform:uppercase;color:var(--mj-gray-400)}
.rb-f-bar .track{height:8px;border-radius:999px;background:var(--mj-surface-2);position:relative;overflow:hidden}
body.dark-mode .rb-f-bar .track{background:#262629}
.rb-f-bar .fill{position:absolute;left:0;top:0;height:100%;border-radius:999px;background:var(--rb-accent)}
.rb-f-bar .fill.is-strong{background:var(--mj-growth)}
.rb-f-bar .val{font-family:var(--font-display);font-weight:800;font-size:.95rem;text-align:right;color:var(--mj-ink)}
body.dark-mode .rb-f-bar .val{color:#f4f4f5}
.rb-f-col h4{font-family:var(--font-meta);font-size:.72rem;letter-spacing:1.5px;text-transform:uppercase; margin:0 0 var(--sp-4);font-weight:700}
.rb-f-col.is-pro h4{color:#0b7a5b}
.rb-f-col.is-con h4{color:var(--mj-alert)}
.rb-f-col ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
.rb-f-col li{display:flex;gap:9px;font-size:.92rem;line-height:1.45;align-items:flex-start}
.rb-f-col li::before{flex-shrink:0;font-family:var(--font-display);font-weight:800;line-height:1.3}
.rb-f-col.is-pro li::before{content:"+";color:#0b7a5b}
.rb-f-col.is-con li::before{content:"–";color:var(--mj-alert)}
.rb-f-fit .cell{padding:var(--sp-6)}
/* NO GREY BAR BESIDE THE RED ONE. Lucky, 2026-08-26: "there is a grey bar and
   a red bar. That feels a bit odd ... people can already tell what the message
   of that element seems to be."
   He is right, and the reason is that the two lines were doing the same job
   twice. The coloured rules already say which column is which — green for the
   fit, red for the mismatch — so a neutral divider between them adds a third
   vertical line that means nothing and reads as a border the red one is
   escaping from. The gap is the divider. */
.rb-f-fit .cell.is-for{border-right:none}
/* THE ACCENT IS NOT A BORDER COLOUR, IT IS THE LABEL — Lucky, 2026-08-26:
   the green rule on "Best for" goes grey in dark mode while the red on "Who
   should avoid it" stays red. This rule was the cause: `border-color` with no
   side named repaints ALL FOUR, so it overwrote the green left accent along
   with the neutral edges it was actually trying to darken. `is-avoid` had no
   such rule, which is the only reason its red survived — the pair looked
   deliberate and was an accident.
   Name the sides you mean. */
body.dark-mode .rb-f-fit .cell.is-for{ border-right-color:#2a2a2d;
  border-bottom-color:#2a2a2d; border-top-color:#2a2a2d }
.rb-f-fit .cell p{margin:0;font-size:.95rem;line-height:1.55;color:var(--mj-charcoal)}
body.dark-mode .rb-f-fit .cell p{color:#c9c9cc}
.rb-f-verdict blockquote{margin:0 0 var(--sp-5);font-family:var(--font-body);font-size:1.1rem; line-height:1.6;color:var(--mj-ink);font-style:italic;border-left:3px solid var(--rb-accent);padding-left:var(--sp-4)}
body.dark-mode .rb-f-verdict blockquote{color:#e9e9ea}
.rb-f-facts{display:flex;flex-wrap:wrap;gap:8px 22px;margin-bottom:var(--sp-5);font-size:.88rem}
.rb-f-facts .f{display:flex;flex-direction:column}
.rb-f-facts .f b{font-family:var(--font-meta);font-size:.64rem;letter-spacing:1px;text-transform:uppercase; color:var(--mj-gray-400);font-weight:700}
.rb-f-facts .f span{font-family:var(--font-display);font-weight:800;color:var(--mj-ink);font-size:1rem}
body.dark-mode .rb-f-facts .f span{color:#f4f4f5}
.rb-f-cta-row{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.rb-f-bar{grid-template-columns:96px 1fr 38px;gap:var(--sp-3)}

/* Promoted from mashjoy-cta-block.html, 2026-08-15 — caught by the rebuilt L0 rule, which
   asks whether a class SHIPS rather than whether its name starts with mj-. */
.scms-cta--inline,.scms-cta--banner,.scms-cta--midfunnel,.reading-sim,.var-label{max-width:100%}

/* ── THE VERDICT CAN BE ACTED ON ───────────────────────────────────────────
   Added 2026-08-16. The segmented verdict is the LAST place a reader decides,
   and it was the only decision surface on the site with nothing to act on —
   it named a machine and sent the reader back up the page to find out what it
   costs and where to get it.

   THE PRICE IDIOM IS THE PRODUCT BOX'S, deliberately. Amount in display weight,
   unit beside it in meta. A second price treatment invented here would be a
   second design of the same fact, and the reader would have to learn it twice.

   The stamp is not optional. A price on a decision surface without the date it
   was checked is the one number on this site that must never appear bare.
   ------------------------------------------------------------------------ */
.mj-rec__price{ margin:6px 0 0; display:flex; align-items:baseline; gap:8px; flex-wrap:wrap }
.mj-rec__price b{ font-family:var(--font-display); font-weight:800; font-size:1.24rem;
  line-height:1.1; letter-spacing:-.02em; color:var(--mj-black) }
body.dark-mode .mj-rec__price b{ color:#fff }
.mj-rec__price .unit{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:.6px;
  text-transform:uppercase; color:var(--mj-gray-400); line-height:1.3 }
/* THE SMALL BUTTON IS STILL A BUTTON. Lucky keeps the compact size deliberately
   — "I do like the button sizes in these sections since they are typically a bit
   smaller than other buttons elsewhere … this helps the system feel intentional"
   — but it was set in `--font-meta`, the LABEL face, so a control was wearing a
   caption's type. Same size, button font. */
.mj-rec__cta{ display:inline-block; margin-top:var(--sp-3); padding:9px 16px;
  font-family:var(--font-display); font-size:.64rem; letter-spacing:1.2px; font-weight:700;
  text-transform:uppercase; text-decoration:none; border-radius:var(--radius-sm);
  background:var(--mj-red); color:#fff; transition:background .15s ease }
.mj-rec__cta:hover{ background:var(--mj-black) }
body.dark-mode .mj-rec__cta:hover{ background:#fff; color:var(--mj-black) }
.mj-rec__stamp{ display:block; margin-top:7px; font-family:var(--font-meta);
  font-size:.55rem; letter-spacing:.8px; text-transform:uppercase; color:var(--mj-gray-400) }

/* The eyebrow's SECOND half is the argument, not the filing.
   "Card machine review · Best if the counter should earn" — the first half says
   what kind of thing this is; the second says WHY WE ARE RECOMMENDING IT, which
   is the whole reason a reader is looking at the block. Set as one flat grey
   line, the argument disappeared into the category.
   It takes ink and weight rather than a colour: the badges beside it are already
   green and the CTA below is already red, and a third accent here would make the
   block argue with itself. */
.scms-rb-eyebrow .rb-why{ color:var(--mj-ink-900); font-weight:700 }
body.dark-mode .scms-rb-eyebrow .rb-why{ color:#f4f4f5 }

/* =============================================================================
   PRODUCTION REVIEW — BATCH 1 (owner observations, 2026-08-17)
   Design authority, Opus 5.

   Six refinements to elements that already work. Every one of them changes how
   an existing component PRESENTS, and none changes what it says.

   THE PLAYBOOK NAMED SIX CLASSES THAT DO NOT EXIST — `.mj-short-answer`,
   `.mj-pull-stat`, `.mj-proscons--branded`, `.mj-author-block`,
   `.mj-progress-bar`, `.mj-inline-toc`. Editing those would have created six
   orphans, which is the exact failure the same document warns about. The real
   names are below.
   ============================================================================= */

/* ── 1.1 · THE SHORT ANSWER'S LABEL ───────────────────────────────────────
   Was `--mj-gray-400`: the same grey as every other meta label on the page, so
   the one element carrying the desk's actual answer announced itself in the
   voice of a caption.

   GREEN, NOT BLUE. The link system already assigns meaning to hue — red costs
   the reader money, blue is a citation, GREEN IS PROOF. The short answer is the
   desk vouching for a conclusion, which is the green job. Blue would also put a
   third colour beside the strip's red rule and make the element argue with
   itself. Understated: a label, not a highlight. */
.mj-vstrip__k{ color:var(--mj-growth-text) }
body.dark-mode .mj-vstrip__k{ color:#4ec98a }

/* ── 1.2 / 1.3 · THE BODY IS NOT ALL BOLD ─────────────────────────────────
   `.mj-vstrip p` was `font-display` at weight 700 — the WHOLE paragraph set in
   display type, bold. Lucky: "the body text must NOT be entirely bold."

   When everything is emphasised, nothing is. A reader scanning a bold block has
   no way in; the eye finds no first thing to land on, so it lands on nothing and
   reads the whole paragraph or skips it.

   So the baseline returns to body type at normal weight, and EMPHASIS BECOMES
   AVAILABLE AGAIN — which is the point. Technor now marks the phrases the writer
   chooses, at the SAME SIZE as the body, exactly as Lucky specified: "keep the
   technor and normal body typography about the same size even though the
   technor will be a bit bold." Accent, never dominance. */
.mj-vstrip > p{ font-family:var(--font-body); font-weight:400; font-size:1.06rem;
  line-height:1.68; color:var(--mj-ink) }
body.dark-mode .mj-vstrip > p{ color:#e8e8ea }
/* THE DATE STAMP INSIDE THE SHORT ANSWER. Bold, so the reader weighing the
   numbers can find it in one pass — but in the BODY face, because the display
   face in this element means "this is the machine". Two different jobs must not
   wear the same emphasis. */
.mj-vstrip__when{ font-family:var(--font-body); font-weight:700; color:var(--mj-ink) }
body.dark-mode .mj-vstrip__when{ color:#fff }
.mj-vstrip b, .mj-vstrip strong{ font-family:var(--font-display); font-weight:700;
  font-size:1em; letter-spacing:-.005em; color:var(--mj-black) }
body.dark-mode .mj-vstrip b, body.dark-mode .mj-vstrip strong{ color:#fff }
/* A PRODUCT NAMED IN THE SHORT ANSWER IS A LINK (c-xoox, 2026-09-24): to its section
   (#anchor) or through the ledger (aff:). Set like the brand names beside it, in the
   display face, with an underline in the house red so it reads as a way in. */
.mj-vstrip > p a{ color:inherit; font-family:var(--font-display); font-weight:700;
  text-decoration:underline; text-decoration-color:var(--mj-red); text-decoration-thickness:1.5px;
  text-underline-offset:3px }
.mj-vstrip > p a:hover{ color:var(--mj-red) }
body.dark-mode .mj-vstrip > p a{ color:#fff }

/* ── 8.1 / 8.2 · THE PULL-STAT MUST NOT JUMP ──────────────────────────────
   It was `display:flex` with `flex-wrap:wrap`, which is the entire bug: a short
   caption sits beside the number and a long one WRAPS UNDERNEATH IT. The element
   changed shape depending on how many words the writer used, so the same
   component looked like two different components on two pages.

       A layout that depends on content length is not a layout.

   Grid instead: the number holds its own column, the caption holds the rest, and
   the arrangement is identical whether the caption is six words or sixty. The
   number scales down a little on the small end so a four-figure rand amount
   still fits its column without forcing a reflow. */
.mj-pullstat{ display:grid; grid-template-columns:minmax(0,auto) minmax(0,1fr);
  align-items:baseline; gap:var(--sp-5); flex-wrap:nowrap }
/* THE STAT SIZES ITSELF TO ITS OWN LENGTH — observation 8.2.
   "If text must be longer, reduce font size slightly (still big enough to read,
    just a better fit)."

   Measured: "No crossover" — twelve characters at 55px, `white-space:nowrap` —
   took 52% OF THE ROW and squeezed its caption to 305px, while a six-character
   "R2,760" leaves the caption nearly double that. Same element, two layouts,
   decided by how many letters the writer happened to use. That is 8.1's "no
   layout jumps because of text overflow".

   Length is DATA the renderer counts; the three sizes are a design decision and
   live here. A number stays huge, because a number is the point; a phrase steps
   down until it fits beside its own explanation. */
.mj-pullstat__n{ font-size:clamp(2.2rem,5.4vw,3.9rem); white-space:nowrap }
.mj-pullstat[data-len="m"] .mj-pullstat__n{ font-size:clamp(1.9rem,4.2vw,3rem) }
.mj-pullstat[data-len="l"] .mj-pullstat__n{ font-size:clamp(1.6rem,3.2vw,2.2rem);
  white-space:normal; line-height:1.05; max-width:11ch }
.mj-pullstat figcaption{ max-width:52ch; align-self:center }
/* 8.3 — the caption is a REFERENCE TOOL, so the facts inside it carry weight:
   a price, a percentage, the date it was checked. The writer marks them; this
   makes the mark mean something. */
.mj-pullstat figcaption b{ font-family:var(--font-display); font-weight:700;
  color:var(--mj-black) }
body.dark-mode .mj-pullstat figcaption b{ color:#fff }
/* THE SOURCE IS A REFERENCE LINE, NOT THE END OF A SENTENCE — observation 8.3.
   It was concatenated onto the body copy, so "mashjoy.com comparison of both
   providers' published rates, checked 25 July 2026" ran on as if it were part of
   the claim. A reader scanning for HOW FRESH this is had to read the whole
   paragraph to find out.

   Its own line, its own size, and the date lifted with `.mj-cap-k` — the same
   treatment the table captions now carry, because it is the same job. */
.mj-pullstat__say{ display:block }
.mj-pullstat__src{ display:block; margin-top:9px; font-family:var(--font-meta);
  font-size:.66rem; letter-spacing:.9px; color:var(--mj-gray-400); line-height:1.5 }
/* prices and percentages inside a stat's explanation are the reference part of
   it, so they carry the caption-key weight rather than sitting in muted grey */
.mj-pullstat__say b{ font-family:var(--font-display); font-weight:700;
  color:var(--mj-ink) }
body.dark-mode .mj-pullstat__say b{ color:#e8e8ea }

.mj-pullstat figcaption .mj-attrib{ display:inline-block; margin-left:6px;
  color:var(--mj-growth-text) }
@media(max-width:560px){
  /* one column on a phone — the number leads, the caption follows. This is a
     STACK BY INTENT, not a wrap by accident, so it is still the same element. */
  .mj-pullstat{ grid-template-columns:1fr; gap:var(--sp-3) }
  .mj-pullstat figcaption{ max-width:none }
}

/* ── 10.1 · THE BYLINE'S DATES ────────────────────────────────────────────
   Published, fee-verified, read time — all set in the same grey. The
   VERIFICATION DATE is the desk's central promise and it was styled like a
   timestamp on a forum post.

   Green again, and for the same reason as the short answer: this is proof. Only
   the fee-verified value takes it, so the colour still means something —
   colouring all three would make it decoration. */
.mj-byline__mi--verified b{ color:var(--mj-growth-text) }
body.dark-mode .mj-byline__mi--verified b{ color:#4ec98a }

/* ── 15.1 · "IN THIS GUIDE" ───────────────────────────────────────────────
   Lucky: "the element works — just make it stand out a bit more." So: presence,
   not redesign. The label takes ink instead of muted grey and gains a short rule
   that ties it to the list beneath, which is what makes a heading read as a
   section marker rather than a caption. */
.mj-toc__h{ color:var(--mj-ink); font-size:.72rem }
body.dark-mode .mj-toc__h{ color:#f0f0f2 }
.mj-toc__h::after{ content:""; flex:1; height:2px; background:var(--mj-ink-900);
  opacity:.14; margin-left:4px }
body.dark-mode .mj-toc__h::after{ background:#fff; opacity:.16 }

/* ── 20.4 · TABLE ROW DENSITY ─────────────────────────────────────────────
   Lucky asked for 1–2px off the top and bottom of each row, "so the tables don't
   feel too long… but I also don't want the tables to feel too compact."

   `--sp-4` is 16px. Taking 2px off each edge is a ~12% reduction in row height
   and about a row and a half back on a ten-row table — enough to feel shorter,
   not enough to feel cramped. The horizontal padding is untouched, because that
   is what keeps a cell readable. */
/* OBSERVATION 20.4 — "reduce the row heights by taking away 1 to 2px from the
   top and bottom edges … I also don't want the tables to feel too compact."
   2px off each edge is 4px a row: on the nine-row one-year-cost table that is
   36px off its height, which is a paragraph's worth of scrolling, while the
   cells keep 10px of breathing room top and bottom. */
.mj-tw th{ padding-top:12px; padding-bottom:12px }
.mj-tw td{ padding-top:12px; padding-bottom:12px }

/* ── HOW REVEALED CONTENT ARRIVES · owner note, 2026-08-21 ───────────────
       "whenever someone clicks any of these open trays … make the way that it
        appears feel a little bit gentle, because right now it feels a little bit
        tactile — when I click a mouse it just comes to your face. If you can add
        a nice entrance animation that feels gentle but efficient."   — Lucky

   A panel that appears instantly reads as a jump: the page height changes under
   the reader's eye in one frame and they lose the line they were on. A short
   fade with a few pixels of travel gives the eye something to follow, and 160ms
   is under the threshold where an animation starts to feel like waiting.

   IT ANIMATES THE ARRIVAL ONLY, never the departure — closing something you
   asked to close should be immediate, and an exit animation is the part people
   mean when they say interfaces feel slow.

   `prefers-reduced-motion` turns it off entirely. A reader who has told their
   operating system that motion makes them unwell is not asking us for taste. */
@keyframes mj-reveal-in{
  from{ opacity:0; transform:translateY(-6px) }
  to{ opacity:1; transform:none }
}
.mj-note--tray[open] > .mj-note__tray,
.mj-ireveal__p:not([hidden]),
.cc-sections[open] .cc-list,
.cc-offers[open] .cc-buylist,
.mj-gloss__wrap.is-open .mj-gloss__pop{
  animation:mj-reveal-in .16s cubic-bezier(.2,.6,.3,1) both }
@media(prefers-reduced-motion:reduce){
  .mj-note--tray[open] > .mj-note__tray,
  .mj-ireveal__p:not([hidden]),
  .cc-sections[open] .cc-list,
  .cc-offers[open] .cc-buylist,
  .mj-gloss__wrap.is-open .mj-gloss__pop{ animation:none }
}

/* ── THE INLINE REVEAL · `.mj-ireveal` ───────────────────────────────────
   Design authority, Opus 5, 2026-08-21. Owner observation 13.3.

       "the Yoco Khumo 2 and iKhokha Flyer Lite entire text could have also been
        a single link that when clicked, it reveals a tray with product boxes …
        the product boxes would CONDITIONALLY APPEAR here if the reader clicked
        the link, if not, content continues to read normal … this will help in
        balancing the content to be informational vs selling, whilst we are still
        able to sell to those who are intentionally curious."

   The reveal tray answers this at BLOCK level: a line of prose the reader may
   open. This answers it at PHRASE level — the product names inside a sentence
   become the trigger, and the sentence still reads normally for everyone who
   does not press them.

   THE PANEL OPENS AT THE PARAGRAPH BREAK, NEVER MID-SENTENCE. Observation 5.1
   says so directly, and it is also the only place it can go without reflowing
   the line the reader is on. So the trigger is inline and the panel is its
   sibling, joined by aria-controls rather than by nesting — which is what a
   <details> cannot do and why this is not one.

   IT WEARS THE STRUCTURE UNDERLINE, not a commercial colour. The phrase does not
   navigate and does not cost money; it opens something on the page the reader is
   already on. Dotting it in red would promise a checkout. */
.mj-ireveal__t{ display:inline; margin:0; padding:0; background:none; border:0;
  border-bottom:2px dotted var(--mj-ink-900); font:inherit; color:inherit;
  font-weight:600; letter-spacing:inherit; cursor:pointer;
  -webkit-appearance:none; appearance:none;
  transition:border-color .14s ease, color .14s ease }
body.dark-mode .mj-ireveal__t{ border-bottom-color:#e8e8ea }
.mj-ireveal__t:hover{ color:var(--mj-red); border-bottom-color:var(--mj-red);
  border-bottom-style:solid }
.mj-ireveal__t:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
.mj-ireveal__t::after{ content:" \25be"; font-size:.8em; opacity:.7 }
.mj-ireveal__t[aria-expanded="true"]::after{ content:" \25b4" }

.mj-ireveal__p{ margin:var(--sp-4) 0; padding:var(--sp-5);
  background:var(--mj-surface); border:1px solid var(--mj-rule);
  border-left:3px solid var(--mj-ink-900) }
body.dark-mode .mj-ireveal__p{ background:#131315; border-color:#2a2a2d;
  border-left-color:#e8e8ea }
.mj-ireveal__p[hidden]{ display:none }
.mj-ireveal__p>*:first-child{ margin-top:0 }
.mj-ireveal__p>*:last-child{ margin-bottom:0 }
/* two products side by side, the same grid the tray uses — one rule, not two */
.mj-ireveal__p--cols{ display:grid;
  grid-template-columns:repeat(var(--ir-cols,2),minmax(0,1fr));
  gap:var(--sp-3); align-items:start }
.mj-ireveal__p .mj-pb{ width:100%; max-width:none; margin:0; transform:none;
  box-shadow:none }
@media(max-width:760px){ .mj-ireveal__p--cols{ grid-template-columns:1fr } }

/* ── THE REVEAL TRAY · `.mj-note--tray` ───────────────────────────────────
   Design authority, Opus 5, 2026-08-17. Owner observations 2.1.1, 2.3, 13.3,
   13.5, 14.1 — FIVE requests for one primitive.

   A note holds prose the reader may skip. A TRAY HOLDS ELEMENTS, and it exists
   so a commercial answer is available to the reader who asks for it and absent
   for the reader who did not.

       Closed, it is one quiet line of prose.
       Open, it is a full-width tray.
       The article reads the same either way.

   RED, BECAUSE THE LINK SYSTEM ALREADY SAYS SO: red is the ACTION colour, the
   one that costs the reader money. `--caveat` also carries red, and that is not
   a collision — a caveat warns and a tray offers, but both are moments where
   the reader's money is at stake, which is what the colour has always meant on
   this site. The MARKER SHAPE separates them: a caveat's square rotates to a
   diamond; a tray's marker grows into a bar, because a drawer opens rather than
   turns.
   ------------------------------------------------------------------------ */
/* A TRAY THAT BREAKS OUT CANNOT SIT IN A CLIPPING BOX.
   `.mj-note` carries `overflow:hidden` so its rounded right corners clip the
   body's background — correct for a note, fatal for a tray. The opened panel
   computed its full 912px and PAINTED 680px of it: both cards sliced down the
   middle, with the reader seeing half a price and half a CTA.

   It is worth naming how this got through. `getBoundingClientRect()` returned
   912 and reported no overflow, because a rect describes the BOX, not what the
   compositor is allowed to draw. Layout measurements cannot see a clip, so the
   geometry checks all passed on a render that was visibly broken. Only a
   screenshot showed it — and the screenshots were coming back blank because the
   page sets `scroll-behavior:smooth` and every capture was mid-animation.

   The border radius goes with the clip. A tray is a drawer that opens past the
   column; square corners are the honest shape for it. */
.mj-note--tray{ border-left-color:var(--mj-red); overflow:visible; border-radius:0 }
.mj-note--tray>summary{ font-family:var(--font-body); font-size:1rem;
  color:var(--mj-ink); font-weight:400 }
body.dark-mode .mj-note--tray>summary{ color:#e8e8ea }
/* The trigger reads as PROSE with a dotted underline — the same SmartCMS
   anchor idiom used for in-article navigation, so a reader who has learned what
   a dotted underline means on one element already knows what it means here. */
/* the tray opts OUT of the cue pill: its own red label plus the underlined
   trigger prose already reads as clickable, and the first tray in any article
   is introduced in the prose (emphasis-and-scanning.md §6). */
.mj-note--tray>summary::after{ content:""; display:block; height:0;
  border:0; padding:0; margin:0 }
.mj-note--tray>summary>span:not(.mj-note__mk):not(.mj-note__lbl),
.mj-note--tray>summary{ text-underline-offset:4px }
.mj-note--tray>summary:hover{ color:var(--mj-red) }
.mj-note--tray .mj-note__mk{ background:var(--mj-red); border-radius:1px;
  transition:width .18s ease, height .18s ease, transform .18s ease }
.mj-note--tray[open] .mj-note__mk{ width:14px; height:4px; transform:none }
.mj-note--tray .mj-note__lbl{ color:var(--mj-red) }

/* THE TRAY BODY holds elements, so it gets a ground of its own and drops the
   prose measure — a product box squeezed into a paragraph's width is the width
   bug again, one level down. */
/* ONE GROUND, NOT TWO. The tray body was `--mj-surface-2` (grey) while the
   trigger above it and the cards inside it were both white, so opening it
   revealed a grey band sandwiched between two white surfaces — three tones for
   one element. Lucky: "the background of the tray is grey, and I feel like it
   should have been white so that it feels more built together."

   The tray now shares the note's own ground, and a border closes the shape: the
   panel is visibly the SAME BOX as the line that opened it, continuing past the
   column rather than a separate grey thing appearing underneath. */
.mj-note__tray{ background:var(--mj-surface); padding:var(--sp-5);
  border:1px solid var(--mj-rule); border-top:0 }
body.dark-mode .mj-note__tray{ background:#131315; border-color:#2a2a2d }
.mj-note__tray>*:first-child{ margin-top:0 }
.mj-note__tray>*:last-child{ margin-bottom:0 }
/* Elements inside a tray do not break out again — the tray IS the break-out. */
.mj-note__tray .mj-tw,
.mj-note__tray .mj-pb,
.mj-note__tray .scms-rb--full,
.mj-note__tray .mj-rec{ width:100%; max-width:none; margin-left:0; transform:none }

/* ── SIDE BY SIDE · `.mj-note__tray--cols` ────────────────────────────────
   Owner observation, 2026-08-20. The trigger read "put the two R699 machines
   SIDE BY SIDE" and the tray stacked them — both boxes at the same left edge,
   one above the other. The element contradicted its own promise, which is the
   one thing a disclosure may never do: the reader paid a click for that
   sentence.

   THE REVEALED CONTENT WIDENS, NOT THE ELEMENT. Lucky: "by wider, I mean the
   elements that are being revealed can be wider, not that the element itself
   should be wide." Closed, the tray is a line of prose in the reading column,
   exactly where it was. Only the opened panel breaks out — which is the same
   conversion-containment rule the header follows, one element down: widen where
   the reader explores, hold the measure where they read.

   THE WIDTH FOLLOWS THE COUNT, because two boxes and three boxes are not the
   same layout problem. `--tray-cols` is a COUNT the renderer knows, not a
   design decision travelling as data: the design decision — grid, card floor,
   break-out, the cap at the viewport — lives here, once. Two cards → ~928px.
   Three → as wide as the viewport allows before the card floor gives.

   `clamp()` guarantees the floor: a one-card tray computes NARROWER than the
   reading column, and a tray that shrinks the column would be a worse bug than
   the one this fixes. */
.mj-note__tray--cols{
  --tray-cols:2;
  --tray-card:420px;
  width:clamp(100%,
    calc(var(--tray-cols) * var(--tray-card)
         + (var(--tray-cols) - 1) * var(--sp-5) + 2 * var(--sp-5)),
    calc(100vw - 2 * var(--sp-5)));
  max-width:none; margin-left:50%; transform:translateX(-50%);
  display:grid; grid-template-columns:repeat(var(--tray-cols), minmax(0,1fr));
  gap:var(--sp-5); align-items:start }
/* the first/last-child margin resets are for a STACK; in a grid every cell is
   both, and the row would collapse its own rhythm */
.mj-note__tray--cols>*{ margin-top:0; margin-bottom:0 }

/* A CARD IN A TRAY IS ALREADY INSIDE A PANEL, so it does not need its own
   shadow or its own heavy frame — that is what made the open tray read as
   bulky. Lucky: "make sure that the elements shown are a little bit shorter in
   design or not too big or don't feel too bulky." The product shot is the
   biggest single block in the card and it is doing the least work here, where
   the reader has already chosen to compare two machines, so it takes a shorter
   frame. */
.mj-note__tray .mj-pb--stacked{ box-shadow:none; border-color:var(--mj-rule) }
.mj-note__tray .mj-pb--stacked .mj-pb__img{ aspect-ratio:16/10; padding:var(--sp-3) }
.mj-note__tray .mj-pb--stacked .mj-pb__img img{ max-height:100%; object-fit:contain }

/* On a phone the column IS the viewport: there is nothing to break out of, the
   transform would push the panel off-screen, and 420px cards cannot sit beside
   each other anyway. One column, no break-out. */
@media(max-width:820px){
  .mj-note__tray--cols{ width:100%; margin-left:0; transform:none;
    grid-template-columns:1fr; gap:var(--sp-4) }
}

/* THE LABEL IS THE CONTROL, so it should look like one.
   Lucky: "make the prices and offers element be like a button, but when they
   click it, it still opens the tray." It already WAS clickable — it sits inside
   the <summary> — but it read as a caption, so nothing told the reader the tray
   was there. Nothing about the mechanism changes: still one <summary>, still no
   JavaScript, still keyboard-operable. */
/* the control wears the button face, like every other button on the site */
.mj-note--tray .mj-note__lbl{ font-family:var(--font-display); font-weight:700;
  color:#fff; background:var(--mj-red);
  padding:5px 11px; border-radius:var(--radius-sm); letter-spacing:1.2px;
  align-self:center; white-space:nowrap;
  transition:background .15s ease, transform .15s ease }
.mj-note--tray>summary:hover .mj-note__lbl{ background:var(--mj-ink-900) }
.mj-note--tray>summary:active .mj-note__lbl{ transform:translateY(1px) }
.mj-note--tray[open] .mj-note__lbl{ background:var(--mj-ink-900) }
.mj-note--tray>summary:focus-visible .mj-note__lbl{ outline:2px solid var(--mj-trust);
  outline-offset:2px }

/* ── WHAT YOU PAY · `.mj-pb__pay` ─────────────────────────────────────────
   Design authority, Opus 5, 2026-08-17. Owner observation 19.

       "the discounts are shown, but they don't seem to be meaningful until
        someone can tell that there is actually a R100 discount … it is
        important for someone to understand what they're being offered or what's
        the convenience of using our link."

   TWO PRICES AND THEIR SOURCES. The left is what anyone pays on the vendor's own
   site; the right is what this reader pays through our link. The gap between
   them is the entire argument for clicking, and it was previously invisible —
   the box showed one number and the reader had no way to know a second one
   existed.

   THE VENDOR PRICE IS STRUCK, NOT SHOUTED. A crossed-out price is a familiar
   retail gesture and it does the work quietly; making it loud would turn a
   reference tool into a sales bark, which is the thing this desk does not do.

   IT RENDERS ONLY WHEN A VERIFIED OFFER EXISTS. There is no fallback that
   estimates, rounds, or infers — a discount is a PRICE CLAIM, the strongest
   commercial claim on this site, and the one that costs most if it is wrong.
   ------------------------------------------------------------------------ */
.mj-pb__pay{ display:flex; align-items:center; gap:var(--sp-2); flex-wrap:nowrap;
  margin:0 0 var(--sp-3); padding:var(--sp-3) var(--sp-4);
  background:var(--mj-surface-2); border-radius:var(--radius-sm) }
body.dark-mode .mj-pb__pay{ background:#17171a }
.mj-pb__pay-col{ display:flex; flex-direction:column; line-height:1.15 }
/* THE STRUCK PRICE IS A REFERENCE POINT, AND IT HAS TO BE READABLE.
   Lucky, 2026-08-26: on a black panel the "before" price is barely there.

   It was mid-grey with a line through it — a colour chosen against WHITE, then
   inherited by every dark surface the element was later composed into. A strike
   already costs legibility; spending the rest of the budget on low contrast
   leaves the number the reader is meant to compare AGAINST as the faintest
   thing in the block.

   One token, `--mj-struck`, so the answer lives in one place and every dark
   surface gets it — the review block's closing panel today, and whatever dark
   context an element is dropped into next. Kept NEUTRAL rather than red: red on
   this site means money lost or risk, and the old price is neither. It is
   simply what you are not paying. */
:root{ --mj-struck:var(--mj-gray-400) }
body.dark-mode{ --mj-struck:#b8b8c4 }
.mj-pb__pay-col b{ font-family:var(--font-display); font-weight:800; font-size:.95rem;
  color:var(--mj-struck); text-decoration:line-through;
  text-decoration-thickness:1.5px; text-decoration-color:color-mix(in srgb, var(--mj-struck) 70%, transparent) }
/* THE SAVING RIDES ON THE PRICE'S OWN LINE, AND THE LABEL PAYS FOR IT.
   Lucky: "always make sure that that offer discount appears in the same row as
   the price... you can make the text that says 'Yoco's price today' to be a bit
   smaller so that we can score a bit of space."
   Measured in the narrowest card the element has — a pair-strip column at
   303px — the line needed 328px and the chip dropped to a row of its own,
   where it reads as a separate announcement rather than as what comes off THIS
   number. The widest thing on that line was never a fact: it was the label
   "iKhokha's price today" at 137px, set in tracked-out uppercase. So the
   scaffolding gives up the space and the two facts keep theirs. */
.mj-pb__pay-src{ font-family:var(--font-meta); font-size:.48rem; letter-spacing:.5px;
  text-transform:uppercase; color:var(--mj-gray-400); margin-top:3px }
/* AND WHEN EVEN THAT IS NOT ENOUGH, THE LABEL WRAPS — THE CHIP DOES NOT.
   Shrinking the type bought 44px in the widest card and 8px in the narrowest,
   which is a fit, not a rule: one longer brand name or an "R1,000 off" and the
   chip drops to its own row again. The column is already a COLUMN, so a label
   that runs to two lines costs a few pixels of height and nothing else —
   whereas a chip that wraps stops being part of the price. So the columns are
   allowed to narrow past their label, and the chip is not allowed to shrink or
   wrap at all. The type sizes above keep it to one line in every real case;
   this is what happens when a case is not one we anticipated. */
.mj-pb__pay-col{ min-width:0 }
/* WHY `nowrap` AND NOT JUST `min-width:0`.
   Allowing the columns to narrow did nothing on its own: a wrapping flex line
   moves an ITEM to a new line before it will shrink one, so the chip still
   left the price and the label never used the room it had been given. Under
   the stress case — "WAPPoint's price today" against "R1,000 off" in a 303px
   card — the row grew from 65px to 101px and the saving floated free again.
   The line the price and its saving share is therefore not allowed to break.
   With the columns free to narrow, the label is what gives, which is the right
   thing to lose: it is the only part of the line that is not a number. */
/* YOUR price is the one that is not struck, and the only one in full ink. */
.mj-pb__pay-col--yours b{ color:var(--mj-black); text-decoration:none; font-size:1.2rem }
body.dark-mode .mj-pb__pay-col--yours b{ color:#fff }
.mj-pb__pay-col--yours .mj-pb__pay-src{ color:var(--mj-growth-text) }
.mj-pb__pay-arrow{ width:14px; height:1.5px; background:var(--mj-gray-300); flex:0 0 auto }
.mj-pb__pay-arrow::after{ content:""; display:block; width:5px; height:5px; margin-left:9px;
  margin-top:-2px; border-top:1.5px solid var(--mj-gray-300);
  border-right:1.5px solid var(--mj-gray-300); transform:rotate(45deg) }
/* The saving, stated as the reader would say it out loud. */
/* WAS WHITE ON RED — the third look for one fact, and the one that competed
   with the button beside it. Only the layout is local now. */
.mj-pb__pay-tag{ margin-left:auto }
/* On a phone the line genuinely has no room for three parts, and there the
   old behaviour is right: the saving drops below the price it belongs to,
   left-aligned under it rather than stranded against the right edge. */
@media(max-width:520px){
  .mj-pb__pay{ flex-wrap:wrap }
  .mj-pb__pay-tag{ margin-left:0; order:3 }
}

/* ═══════════════════════════════════════════════════════════════════════════
   REVIEW BLOCK · FULL — REBUILT 2026-08-24 (Opus 5, on Lucky's design brief)
   ---------------------------------------------------------------------------
   WHAT CHANGED AND WHY
     · Brand green #0DB980 top AND bottom border. The block now reads as a monetisation
       asset (same family as the product box and CTA elements), and the closing
       border tells the reader where the review ends.
     · The per-axis progress bars are GONE. They ate vertical space, and nobody
       buys a card machine because a "Hardware 4.3" bar is 86% full. That is a
       generic-affiliate reflex, not information.
     · "What we like" is FULL WIDTH, never a narrow column beside the cons.
     · "Where it falls short" is COLLAPSED by default. The honest bit is always
       present and always one click away — never hidden, never shouted.
     · Specs are collapsed too: most readers arrive already knowing them, and the
       vendor's own page lists them anyway.
     · BEST FOR / WHO SHOULD AVOID / ALTERNATIVES sit prominently under the head,
       because that is the sentence the reader actually came for. Alternatives are
       in-article anchors, so a reader who is wrong for this product lands on the
       right one instead of leaving.
     · The close is a dark band: verdict line, what our link is worth, then the
       action. A deliberate destination rather than a banner.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{ --rb-green:#0DB980; --rb-green-ink:#0a7d56; --rb-green-soft:rgba(13,185,128,.09); --rb-red-soft:rgba(200,32,32,.06) }

/* ── the frame ─────────────────────────────────────────────────────────── */
.scms-rb--full{border-top:4px solid var(--rb-frame,var(--rb-green));border-bottom:4px solid var(--rb-frame,var(--rb-green))}
body.dark-mode .scms-rb--full{border-top-color:var(--rb-frame,var(--rb-green));border-bottom-color:var(--rb-frame,var(--rb-green))}

/* ── rank + dynamic recommendation label ───────────────────────────────── */
.scms-rb--full .rb-f-rank{display:flex;align-items:center;gap:var(--sp-3);margin:0;
  padding:var(--sp-4) var(--sp-6) 0;font-family:var(--font-meta)}
.scms-rb--full .rb-f-rank__num{display:inline-flex;align-items:center;justify-content:center;
  min-width:30px;height:30px;padding:0 8px;border-radius:var(--radius-sm);
  background:var(--rb-frame,var(--rb-green));color:#fff;font-family:var(--font-display);font-size:1rem;line-height:1}
.scms-rb--full .rb-f-rank__label{font-family:var(--font-display);font-size:.78rem;letter-spacing:.07em;
  text-transform:uppercase;font-weight:700;color:var(--rb-green-ink)}
body.dark-mode .scms-rb--full .rb-f-rank__label{color:#3ee0a6}

/* ── head: meta left, a BIGGER product shot right ──────────────────────── */
.scms-rb--full .rb-f-head{align-items:center}
.scms-rb--full .rb-f-headmeta{order:1;flex:1 1 auto;min-width:0}
.scms-rb--full .rb-f-visual{order:2;width:200px;height:200px}
@media (max-width:640px){
  .scms-rb--full .rb-f-head{flex-direction:column}
  .scms-rb--full .rb-f-visual{order:-1;width:100%;height:180px}
  .scms-rb--full .rb-f-rank{padding-left:var(--sp-5);padding-right:var(--sp-5)}
}

/* ── likes / shortfalls: stacked, not side by side ─────────────────────── */
.scms-rb--full .rb-f-proscons{display:block;padding:var(--sp-5) var(--sp-6)}
.scms-rb--full .rb-f-proscons .rb-f-col{width:100%}
.scms-rb--full .rb-f-col.is-pro{background:var(--rb-green-soft);border-left:3px solid var(--rb-green);
  border-radius:var(--radius-sm);padding:var(--sp-4) var(--sp-5)}
.scms-rb--full .rb-f-col.is-pro h4{color:var(--rb-green-ink);font-family:var(--font-display);font-size:.8rem;letter-spacing:.06em}
body.dark-mode .scms-rb--full .rb-f-col.is-pro h4{color:#3ee0a6}
.scms-rb--full .rb-f-col.is-con{background:var(--rb-red-soft);border-left:3px solid var(--mj-red);
  border-radius:var(--radius-sm);padding:var(--sp-4) var(--sp-5);margin-top:var(--sp-3)}
.scms-rb--full .rb-f-col.is-con h4{position:absolute;left:-9999px}   /* the summary already says it */

/* ── the two toggles ──────────────────────────────────────────────────── */
.scms-rb--full .rb-f-short,
.scms-rb--full .rb-f-specs{margin-top:var(--sp-3);border-top:1px dashed var(--mj-gray-200);padding-top:var(--sp-3)}
body.dark-mode .scms-rb--full .rb-f-short,
body.dark-mode .scms-rb--full .rb-f-specs{border-top-color:#2f2f33}
.scms-rb--full .rb-f-short__sum,
.scms-rb--full .rb-f-specs__sum{cursor:pointer;list-style:none;display:flex;align-items:center;
  gap:var(--sp-3);font-family:var(--font-display);font-size:.95rem;color:var(--mj-ink);
  padding:6px 0}
body.dark-mode .scms-rb--full .rb-f-short__sum,
body.dark-mode .scms-rb--full .rb-f-specs__sum{color:#ededed}
.scms-rb--full .rb-f-short__sum::-webkit-details-marker,
.scms-rb--full .rb-f-specs__sum::-webkit-details-marker{display:none}
.scms-rb--full .rb-f-short__sum::before,
.scms-rb--full .rb-f-specs__sum::before{content:"+";display:inline-flex;align-items:center;
  justify-content:center;width:20px;height:20px;border-radius:4px;flex:0 0 20px;
  background:var(--mj-gray-200);color:var(--mj-ink);font-family:var(--font-meta);line-height:1}
.scms-rb--full .rb-f-short__sum::before{background:var(--mj-red);color:#fff}
.scms-rb--full details[open] .rb-f-short__sum::before,
.scms-rb--full details[open] .rb-f-specs__sum::before{content:"\2212"}
body.dark-mode .scms-rb--full .rb-f-specs__sum::before{background:#2f2f33;color:#ededed}
.scms-rb--full .rb-f-short__hint{margin-left:auto;font-family:var(--font-meta);font-size:.66rem;
  letter-spacing:.08em;text-transform:uppercase;color:var(--mj-gray-400)}

/* ── specs list inside its toggle ─────────────────────────────────────── */
.scms-rb--full .rb-f-speclist{list-style:none;margin:var(--sp-3) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:2px}
.scms-rb--full .rb-f-speclist li{display:flex;justify-content:space-between;gap:var(--sp-3);
  padding:7px 10px;background:var(--mj-gray-100);border-radius:4px}
body.dark-mode .scms-rb--full .rb-f-speclist li{background:#1d1d20}
.scms-rb--full .rb-f-speclist .k{font-family:var(--font-meta);font-size:.68rem;letter-spacing:.06em;
  text-transform:uppercase;color:var(--mj-gray-400)}
.scms-rb--full .rb-f-speclist .v{font-family:var(--font-display);font-size:.86rem;color:var(--mj-ink)}
body.dark-mode .scms-rb--full .rb-f-speclist .v{color:#ededed}

/* ── best for / who should avoid / alternatives ───────────────────────── */
.scms-rb--full .rb-f-fit{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-5);
  padding:var(--sp-5) var(--sp-6);border-top:1px solid var(--mj-gray-200)}
body.dark-mode .scms-rb--full .rb-f-fit{border-top-color:#2a2a2d}
@media (max-width:640px){ .scms-rb--full .rb-f-fit{grid-template-columns:1fr} }
.scms-rb--full .rb-f-fit .cell.is-for .scms-rb-eyebrow{color:var(--mj-trust)}
.scms-rb--full .rb-f-fit .cell.is-avoid .scms-rb-eyebrow{color:var(--mj-red)}
.scms-rb--full .rb-f-alts{margin:var(--sp-3) 0 0;display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.scms-rb--full .rb-f-alts__k{font-family:var(--font-meta);font-size:.66rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--mj-gray-400)}
.scms-rb--full .rb-f-alts a{font-family:var(--font-display);font-size:.86rem;color:var(--rb-green-ink);
  border-bottom:1px solid currentColor;text-decoration:none}
body.dark-mode .scms-rb--full .rb-f-alts a{color:#3ee0a6}

/* ── the close: dark band, price, action ──────────────────────────────── */
.scms-rb--full .rb-f-close{background:#121214;color:#f2f2f2;padding:var(--sp-5) var(--sp-6)}
.scms-rb--full .rb-f-closeline{margin:0 0 var(--sp-4);font-family:var(--font-body);font-size:1rem;
  line-height:1.55;color:#e8e8e8}
.scms-rb--full .rb-f-close .rb-f-pay{margin:0 0 var(--sp-4)}
.scms-rb--full .rb-f-close .scms-rb-checked{color:#9a9a9a}
.scms-rb--full .rb-f-close .scms-rb-src{color:#c9c9c9}
.scms-rb--full .rb-f-cta-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-3)}
.scms-rb--full .rb-f-close .scms-rb-cta.is-ghost{color:#f2f2f2;border-color:#4a4a4f;background:transparent}

/* ── REVIEW BLOCK v2 · POLISH PASS 2026-08-24 (Lucky's design review) ─────
   Brand green #0DB980 throughout; the toggles read as controls rather than
   footnotes; the fit labels are back (they had been eaten by the repeat
   template); the specs table is legible; and the close invites the action. */

/* the two toggles: a real control, in the display face */
.scms-rb--full .rb-f-short__sum,
.scms-rb--full .rb-f-specs__sum{font-family:var(--font-display);font-weight:700;
  font-size:1rem;letter-spacing:.01em;padding:10px 0}
.scms-rb--full .rb-f-short__sum::before,
.scms-rb--full .rb-f-specs__sum::before{width:24px;height:24px;flex:0 0 24px;
  font-family:var(--font-display);font-weight:800;font-size:1.05rem;border-radius:6px}
.scms-rb--full .rb-f-specs__sum::before{background:var(--mj-ink);color:#fff}
body.dark-mode .scms-rb--full .rb-f-specs__sum::before{background:#3a3a40;color:#fff}
.scms-rb--full .rb-f-short__sum:hover,
.scms-rb--full .rb-f-specs__sum:hover{color:var(--rb-green-ink)}
.scms-rb--full .rb-f-short__sum:focus-visible,
.scms-rb--full .rb-f-specs__sum:focus-visible{outline:2px solid var(--rb-green);
  outline-offset:3px;border-radius:6px}
.scms-rb--full .rb-f-short__hint{font-family:var(--font-meta);font-weight:600;
  font-size:.64rem;letter-spacing:.1em}

/* ONE border, not two: the body wrapper carries no styling of its own */
.scms-rb--full .rb-f-short__body{margin-top:var(--sp-3)}
.scms-rb--full .rb-f-col.is-con{margin-top:0}
.scms-rb--full .rb-f-col.is-con h4{position:static;left:auto;font-family:var(--font-display);
  font-size:.8rem;letter-spacing:.06em;color:var(--mj-red)}

/* specs: a readable two-column table, not thin grey text */
.scms-rb--full .rb-f-speclist li{padding:9px 12px;border-radius:6px;align-items:baseline}
.scms-rb--full .rb-f-speclist .k{font-family:var(--font-meta);font-weight:700;font-size:.66rem;
  letter-spacing:.09em;color:var(--mj-gray-400)}
body.dark-mode .scms-rb--full .rb-f-speclist .k{color:#9c9ca4}
.scms-rb--full .rb-f-speclist .v{font-family:var(--font-display);font-weight:700;font-size:.95rem;
  color:var(--mj-ink)}

/* badges: display face, and they must never collide with the score */
.scms-rb--full .rb-f-bigscore{display:flex;flex-wrap:wrap;align-items:center;
  gap:8px 10px;margin:var(--sp-3) 0}
.scms-rb--full .rb-f-bigscore .scms-rb-badge{font-family:var(--font-display);font-weight:700;
  font-size:.72rem;letter-spacing:.03em;text-transform:none;white-space:nowrap;
  padding:5px 11px;border-radius:999px;margin-top:0}
.scms-rb--full .scms-rb-score b{font-family:var(--font-display);font-size:1.9rem;line-height:1}

/* the fit labels — the question, restored, above its answer */
.scms-rb--full .rb-f-fitk{margin:0 0 6px;font-family:var(--font-meta);font-weight:700;
  font-size:.66rem;letter-spacing:.12em;text-transform:uppercase}
.scms-rb--full .cell.is-for .rb-f-fitk{color:var(--rb-green-ink)}
.scms-rb--full .cell.is-avoid .rb-f-fitk{color:var(--mj-red)}
body.dark-mode .scms-rb--full .cell.is-for .rb-f-fitk{color:#3ee0a6}
.scms-rb--full .rb-f-fitline{margin:0;font-family:var(--font-body);font-size:.95rem;
  line-height:1.5;color:var(--mj-ink);text-transform:none;letter-spacing:0}
body.dark-mode .scms-rb--full .rb-f-fitline{color:#dcdce0}
.scms-rb--full .cell.is-for{border-left:3px solid var(--rb-green);padding-left:var(--sp-4)}
.scms-rb--full .cell.is-avoid{border-left:3px solid var(--mj-red);padding-left:var(--sp-4)}

/* the close: give the action a voice */
.scms-rb--full .rb-f-ctahead{margin:0 0 var(--sp-3);font-family:var(--font-display);
  font-weight:800;font-size:1.15rem;color:#fff;letter-spacing:.01em}
.scms-rb--full .rb-f-close .scms-rb-cta{font-family:var(--font-display);font-weight:700}
.scms-rb--full .rb-f-close .scms-rb-cta:not(.is-ghost){background:var(--mj-red);color:#fff;
  border-color:var(--mj-red)}
.scms-rb--full .rb-f-close .scms-rb-cta:not(.is-ghost):hover{filter:brightness(1.08)}
.scms-rb--full .rb-f-rank__num{font-weight:800}

/* ═══════════════════════════════════════════════════════════════════════════
   PRODUCT BOX · PHASE 2 REFINEMENTS — 2026-08-24 (Lucky's design review)
   "More useful information + less vertical space + stronger hierarchy +
    easier scanning." Height is the enemy: a box that makes the reader scroll
   is a box they scroll past.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. THE PRICE, UNBOXED ─────────────────────────────────────────────────
   It sat in a grey panel inside the card — a container inside a container,
   which capped how large the number could be and fenced it off from the
   margins it should own. The panel is gone; a hairline rule does the same
   separating job for none of the height, and the figures grow into the space. */
.mj-pb__pay{ background:none; padding:var(--sp-3) 0; margin:var(--sp-3) 0;
  border-top:1px solid var(--mj-gray-200); border-bottom:1px solid var(--mj-gray-200);
  gap:var(--sp-4) }
body.dark-mode .mj-pb__pay{ background:none; border-top-color:#2a2a2d; border-bottom-color:#2a2a2d }
.mj-pb__pay-col b{ font-size:1.08rem }
.mj-pb__pay-col--yours b{ font-size:1.46rem; letter-spacing:-.01em }
.mj-pb__pay-src{ font-size:.52rem; letter-spacing:.6px }
/* A PRICE IS STRUCK ONLY WHEN SOMETHING REPLACES IT (fixed 2026-09-16).
   The struck style was written for the discount pair — "Yoco's price today"
   crossed out beside "your price" — and applied to every first column. A box
   with no offer has only that first column, so its one real price rendered
   crossed out: all seven prices on the solar ranking and four of nine on card
   machines, each reading as "not this". When there is no second column, the
   first column IS the price, and it takes the price's weight. */
.mj-pb__pay:not(:has(.mj-pb__pay-col--yours)) .mj-pb__pay-col b{
  color:var(--mj-black); text-decoration:none; font-size:1.46rem; letter-spacing:-.01em }
body.dark-mode .mj-pb__pay:not(:has(.mj-pb__pay-col--yours)) .mj-pb__pay-col b{ color:#fff }
.rb-f-close .mj-pb__pay:not(:has(.mj-pb__pay-col--yours)) .mj-pb__pay-col b{ color:#fff }

/* ── 2. "AND EVERYTHING <BRAND> GIVES EVERYONE" — a control, not a footnote ─
   It was 0.7rem uppercase grey meta with the browser's default marker: it read
   as small print, so nobody opened it. Display face, brand green, and a marker
   that says "there is more here". */
.mj-getwhat__sum{ font-family:var(--font-display); font-weight:700; font-size:.82rem;
  letter-spacing:.01em; text-transform:none; color:var(--mj-growth-text);
  list-style:none; display:flex; align-items:center; gap:8px; padding:4px 0 }
.mj-getwhat__sum::-webkit-details-marker{ display:none }
.mj-getwhat__sum::before{ content:"+"; display:inline-flex; align-items:center;
  justify-content:center; width:18px; height:18px; flex:0 0 18px; border-radius:5px;
  background:var(--mj-growth-text); color:#fff; font-family:var(--font-display);
  font-weight:800; font-size:.8rem; line-height:1 }
.mj-getwhat__more[open] .mj-getwhat__sum::before{ content:"\2212" }
.mj-getwhat__sum:hover{ filter:brightness(1.15) }
.mj-getwhat__sum:focus-visible{ outline:2px solid var(--mj-growth-text); outline-offset:3px; border-radius:5px }
body.dark-mode .mj-getwhat__sum{ color:#3ee0a6 }
body.dark-mode .mj-getwhat__sum::before{ background:#3ee0a6; color:#08301f }

/* the folded list uses the SAME marker family as the list above it — a rounded
   green square. Two different bullet shapes in one element is two designers. */
.mj-getwhat__si{ padding-left:18px }
.mj-getwhat__si::before{ content:""; position:absolute; left:0; top:.45em;
  width:7px; height:7px; border-radius:2px; background:var(--mj-growth-text); opacity:.55 }
body.dark-mode .mj-getwhat__si::before{ background:#3ee0a6; opacity:.5 }

/* ── 2b. THE TWO HEADINGS ARE A HIERARCHY, NOT A PAIR ──────────────────────
   Lucky, 2026-08-26: make "what you get with the extra Yoco discount" Technor
   bold and "slightly bigger than the 'and everything Yoco gives everyone' text,
   just to add that subtle psychological attention draw."

   That is the right instinct and it is worth stating as a rule: these two lines
   are NOT siblings. One is what our route adds — the reason the reader is still
   on this page rather than the vendor's. The other is what they would have got
   anyway. Equal weight would have flattened an argument into a menu, so the
   gain leads at .9rem and the given follows at .82rem, both in the display face
   so the step reads as emphasis rather than as two different elements. */
.mj-getwhat__k{ font-family:var(--font-display); font-weight:800; font-size:.9rem;
  letter-spacing:.005em; text-transform:none; color:var(--mj-growth-text);
  margin:0 0 9px }

/* THE TOGGLED LIST LINES UP WITH THE LIST ABOVE IT. The summary carries an
   18px marker box plus an 8px gap, so its contents were indented 26px from the
   ticks they belong under and the panel read as two columns. The `<details>`
   pulls its body back to the element's own left edge; only the SUMMARY keeps
   the marker offset, because the marker is the control. */
.mj-getwhat__more{ margin:10px 0 0 }
.mj-getwhat__more > .mj-getwhat__std{ margin-left:0; padding-left:0 }
.mj-getwhat__std{ margin:8px 0 0 }

/* ── 2c. THE PRICE HAS TO BE READABLE ON THE DARK PANEL ────────────────────
   Lucky, 2026-08-26: "fix the colour issue, an issue that's making the pricing
   text not to be visible."

   The pay block is a LIGHT-SURFACE element — near-black figures, grey labels —
   and the review block's closing panel is #121214. So the reader's own price,
   the single most important number in the element, was rendering #0a0a0a on
   #121214: present, correct, and invisible. This is the cost of composing one
   atom into two surfaces, and the fix belongs to the SURFACE, not the atom. */
.rb-f-close .mj-pb__pay{ border-top-color:rgba(255,255,255,.16);
  border-bottom-color:rgba(255,255,255,.16) }
/* The blanket rule that made every figure in this panel near-white also caught
   the STRUCK price, so the old price and the reader's price ended up the same
   weight and colour — the comparison the element exists to draw, undrawn. The
   struck one takes the token; the rest stay bright. */
.rb-f-close .mj-pb__pay-col b{ color:#f4f4f6 }
.rb-f-close .mj-pb__pay-col:first-child b{ color:var(--mj-struck) }
/* The closing panel is dark in BOTH themes, so it sets the token itself rather
   than relying on body.dark-mode — an element that is dark on a light page is
   exactly the case the old rule got wrong. */
.rb-f-close{ --mj-struck:#b8b8c4 }
.rb-f-close .mj-pb__pay-src{ color:#9a9aa2 }
.rb-f-close .mj-pb__pay-col--yours b{ color:#fff }
.rb-f-close .mj-pb__pay-col--yours .mj-pb__pay-src{ color:#3ee0a6 }
.rb-f-close .mj-pb__pay-arrow{ opacity:.5 }
.rb-f-close .mj-pb__pay-tag{ background:rgba(62,224,166,.16); color:#3ee0a6;
  border-color:rgba(62,224,166,.32) }

/* THE VERDICT GETS A LABEL, and the discount gets a sentence.
   The closing line was the strongest thing in the block and arrived with no
   heading, so a scanning reader met it as one more paragraph. The pay note
   fills the gap between the numbers and the buttons with the one thing the
   numbers cannot say: that the reader does nothing to earn it. */
.rb-f-verdictk{ font-family:var(--font-meta); font-size:.64rem; letter-spacing:1.6px;
  text-transform:uppercase; color:#3ee0a6; margin:0 0 6px }
.rb-f-paynote{ margin:8px 0 0; font-size:.78rem; line-height:1.5; color:#a8a8b0 }
.rb-f-paynote b,.rb-f-paynote strong{ color:#fff }

/* ── 3. ACTIONS STAY ON ONE LINE ───────────────────────────────────────────
   A full-width CTA stacked its sibling underneath and added a whole row of
   height per box. They share the row and only break when the space genuinely
   cannot hold them. */
.mj-pb__foot{ display:flex; flex-wrap:wrap; align-items:center; gap:10px }
.mj-pb.mj-pb--stacked .mj-pb__cta{ width:auto; flex:1 1 auto; min-width:150px }
.mj-pb.mj-pb--stacked .mj-pb__disclosure{ flex:1 0 100%; margin:0 }
.mj-pb__actions{ display:flex; flex-wrap:wrap; gap:10px; align-items:center }
/* THE BUTTONS SHARE A ROW; EVERYTHING ELSE OWNS ONE. Lucky, 2026-08-26: "the
   buttons don't stack, they should always be horizontal." They stacked because
   every child here is a flex sibling — pay block, benefits panel, two buttons,
   two lines of small print — so the moment a long ghost label pushed the pair
   past the column, the second fell to its own line and the box grew a staircase.
   Naming the full-width members leaves the buttons a row to share, and they
   shrink together instead of falling apart. */
.mj-pb__actions > .mj-pb__pay,
.mj-pb__actions > .mj-getwhat,
.mj-pb__actions > .mj-pb__specwrap,
.mj-pb__actions > .mj-pb__disclosure,
.mj-pb__actions > .mj-pb__verified{ flex:0 0 100% }
.mj-pb__actions > .mj-pb__cta{ flex:1 1 0; min-width:0; padding-inline:14px;
  text-align:center; hyphens:auto;
  /* Equal height, so a two-line label does not leave its partner floating in
     the middle of the row. Sharing a row is only half of looking like a pair. */
  align-self:stretch }

/* ── 4. STACKED COMPACT, ACTUALLY COMPACT ──────────────────────────────────
   Roughly a third of its height came from image box, generous padding and a
   full-width button. Same information, tighter frame. */
.mj-pb.mj-pb--stacked{ padding:var(--sp-4) }
.mj-pb.mj-pb--stacked .mj-pb__img{ height:132px; margin-bottom:var(--sp-3) }
.mj-pb.mj-pb--stacked .mj-pb__name{ font-size:1.12rem; margin:2px 0 4px }
.mj-pb.mj-pb--stacked .mj-pb__hook{ font-size:.86rem; line-height:1.45; margin:0 0 var(--sp-3) }
.mj-pb.mj-pb--stacked .mj-pb__eyebrow{ font-size:.62rem; letter-spacing:1.1px }
.mj-pb.mj-pb--stacked .mj-pb__specs{ gap:1px; margin:var(--sp-3) 0 }
.mj-pb.mj-pb--stacked .mj-pb__specs li{ padding:5px 9px }
.mj-pb.mj-pb--stacked .mj-pb__price{ font-size:1.15rem; margin:0 }
.mj-pb.mj-pb--stacked .mj-pb__badges{ margin-bottom:4px }

/* ── 5. THE SIDE-BY-SIDE STRIP · `.mj-pb-grid--n` [NEW] ────────────────────
   For putting 2 to 5 products beside each other in a list or comparison
   article — the visual equivalent of a table row, built from real product
   boxes so the price, the offer and the link stay live rather than becoming
   flat cells. Column count is data (`--n`), so one rule serves every count,
   and it collapses to a scroll-strip on narrow screens rather than crushing
   five cards into a phone. */
.mj-pb-grid--n{ display:grid; gap:14px; margin:var(--sp-5) 0;
  grid-template-columns:repeat(var(--n,3), minmax(0,1fr)) }
.mj-pb-grid--n > .mj-pb{ margin:0; height:100% }
@media (max-width:900px){
  .mj-pb-grid--n{ grid-auto-flow:column; grid-auto-columns:minmax(240px,78%);
    grid-template-columns:none; overflow-x:auto; scroll-snap-type:x mandatory;
    padding-bottom:6px; -webkit-overflow-scrolling:touch }
  .mj-pb-grid--n > .mj-pb{ scroll-snap-align:start }
}

/* ═══════════════════════════════════════════════════════════════════════════
   JUMP NAV · `.a-jump` — PROMOTED TO THE LIBRARY 2026-08-24
   It lived only in the list prototype's own <style> block, which means it could
   never render on the site: the library is the law, and a class with no library
   rule ships unstyled. Promoted, and given the card treatment the finder uses
   (coloured top rule, hairline frame, faint shade) because Lucky reads that as
   "this is a control, use it".
   HORIZONTAL BY DESIGN. A vertical list of eleven machines is the article
   again; a wrapped row of chips costs four lines and is scannable in one pass.
   ═══════════════════════════════════════════════════════════════════════════ */
.a-jump{ max-width:var(--article-width); margin:var(--sp-6) auto;
  padding:var(--sp-4) var(--sp-5); border:1px solid var(--mj-rule);
  border-top:3px solid var(--rb-green); border-radius:var(--radius-md);
  background:var(--rb-green-soft);
  box-shadow:0 1px 2px rgba(10,10,10,.04), 0 8px 24px -12px rgba(10,10,10,.10) }
body.dark-mode .a-jump{ background:rgba(13,185,128,.07); border-color:#2a2a2d;
  border-top-color:var(--rb-green); box-shadow:none }
.a-jump .jt{ font-family:var(--font-display); font-weight:700; font-size:.8rem;
  letter-spacing:.02em; text-transform:none; color:var(--rb-green-ink); margin:0 0 10px }
body.dark-mode .a-jump .jt{ color:#3ee0a6 }
.a-jump ul{ list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap; gap:8px }
.a-jump li{ margin:0 }
.a-jump a{ display:inline-flex; align-items:center; gap:7px; padding:6px 12px 6px 7px;
  border-radius:999px; background:var(--mj-white); border:1px solid var(--mj-gray-200);
  font-family:var(--font-display); font-weight:600; font-size:.84rem;
  color:var(--mj-charcoal); text-decoration:none; transition:none }
body.dark-mode .a-jump a{ background:#17171a; border-color:#2f2f33; color:#dcdce0 }
.a-jump a:hover{ border-color:var(--rb-green); color:var(--rb-green-ink) }
body.dark-mode .a-jump a:hover{ color:#3ee0a6 }
.a-jump a:focus-visible{ outline:2px solid var(--rb-green); outline-offset:2px }
.a-jump__n{ display:inline-flex; align-items:center; justify-content:center;
  min-width:20px; height:20px; padding:0 5px; border-radius:6px;
  background:var(--rb-green); color:#fff; font-family:var(--font-display);
  font-weight:800; font-size:.72rem; line-height:1; flex:0 0 auto }

/* ═══════════════════════════════════════════════════════════════════════════
   RECAP · `.mj-recap` — "what we've covered so far"  [NEW 2026-08-24]
   ---------------------------------------------------------------------------
   Placed after a GROUP of recommendations, not after every product. By the time
   a reader is four machines deep they have stopped holding the earlier ones in
   their head, and the honest question is "which was the cheap one again?".
   This answers it without making them scroll back and lose their place.

   NOT a comparison table: that element is TRANSPOSED for this job — its rows
   are attributes and its columns are products. A recap is one row per product,
   because the reader is scanning for a product, not for an attribute.

   Four jobs per row: name it, say who it is for, show what it costs, and offer
   both ways out — back to the review, or straight to the vendor.
   ═══════════════════════════════════════════════════════════════════════════ */
/* THE RECAP IS A NAVIGATION AID, SO IT LOOKS LIKE ONE — Lucky, 2026-08-26:
   make its top border and text "the mashjoy.com element green. Black feels a
   bit dead", and make it feel like the jump nav.

   He is reading the page correctly. The recap and the jump nav do the SAME JOB
   from opposite directions — one says where you can go, the other says where
   you have been — and they sat in different visual languages: green rule and
   green heading on a soft tint for one, black rule and black heading on plain
   surface for the other. A reader has no way to learn that these are a pair
   when only one of them is coloured. Same rule weight, same ink, same soft
   ground, so the two read as the article's navigation furniture. */
.mj-recap{ max-width:var(--article-width); margin:var(--sp-6) auto;
  border:1px solid var(--mj-rule); border-top:3px solid var(--rb-green);
  border-radius:var(--radius-md); background:var(--rb-green-soft);
  padding:var(--sp-4) var(--sp-5) var(--sp-3);
  box-shadow:0 1px 2px rgba(10,10,10,.04), 0 8px 24px -12px rgba(10,10,10,.10) }
/* WHITE GROUND, GREEN FURNITURE — Lucky, 2026-08-26. The recap borrows the jump
   nav's rule and ink because they are a pair, but not its tint: the nav is a
   short strip of chips, while this is a five-row table of prices, and a table
   reads best off white. The green stays where it labels — the rule and the
   heading. */
.mj-recap{ background:#fff }
body.dark-mode .mj-recap{ background:#161618; border-color:#2a2a2d;
  border-top-color:var(--rb-green); box-shadow:none }
.mj-recap__k{ font-family:var(--font-display); font-weight:700; font-size:.8rem;
  letter-spacing:.02em; color:var(--rb-green-ink); margin:0 0 var(--sp-3) }
body.dark-mode .mj-recap__k{ color:#3ee0a6 }

/* wide content scrolls INSIDE its own container, never the page body */
.mj-recap__scroll{ overflow-x:auto; -webkit-overflow-scrolling:touch }
.mj-recap__t{ width:100%; border-collapse:collapse; font-size:.88rem }
.mj-recap__t th{ text-align:left; font-family:var(--font-meta); font-weight:700;
  font-size:.62rem; letter-spacing:.11em; text-transform:uppercase;
  color:var(--mj-gray-400); padding:0 var(--sp-3) 7px 0; white-space:nowrap;
  border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-recap__t th{ border-bottom-color:#2a2a2d; color:#9c9ca4 }
.mj-recap__t td{ padding:10px var(--sp-3) 10px 0; vertical-align:middle;
  border-bottom:1px solid var(--mj-gray-100) }
body.dark-mode .mj-recap__t td{ border-bottom-color:#232326 }
.mj-recap__t tr:last-child td{ border-bottom:0 }
.mj-recap__hide{ position:absolute; left:-9999px }

/* the weight goes on the FIGURE and the NAME — the ladder the table library uses */
.mj-recap__p{ white-space:nowrap }
.mj-recap__pl,.mj-recap__p a{ font-family:var(--font-display); font-weight:700; font-size:.92rem;
  color:var(--mj-ink); text-decoration:none; border-bottom:1px solid transparent }
/* A DARK-MODE PREFIX BINDS ONLY TO THE SELECTOR IT IS ATTACHED TO.
   This read `body.dark-mode .mj-recap__pl, .mj-recap__p a` — so the second
   selector was UNSCOPED and painted the product link #ededed in LIGHT mode as
   well, on a #fdfdfd panel. The machine names in "what we've covered so far"
   were near-white on near-white: present, linked, clickable, invisible. This is
   the one CSS mistake a comma can make on its own, and it is worth knowing by
   sight, because nothing about the rule looks wrong. */
body.dark-mode .mj-recap__pl,
body.dark-mode .mj-recap__p a{ color:#ededed }
.mj-recap__pl:hover,.mj-recap__p a:hover{ border-bottom-color:var(--rb-green) }
.mj-recap__n{ display:inline-flex; align-items:center; justify-content:center;
  min-width:19px; height:19px; padding:0 5px; margin-right:8px; border-radius:5px;
  background:var(--rb-green); color:#fff; font-family:var(--font-display);
  font-weight:800; font-size:.68rem; line-height:1 }
.mj-recap__f{ color:var(--mj-text-muted); font-size:.85rem; line-height:1.35 }
body.dark-mode .mj-recap__f{ color:#b4b4ba }
.mj-recap__pr{ font-family:var(--font-display); font-weight:700; white-space:nowrap;
  color:var(--mj-ink) }
body.dark-mode .mj-recap__pr{ color:#f4f4f5 }

/* both ways out: back to what they read, or on to the vendor */
.mj-recap__a{ white-space:nowrap; text-align:right }
.mj-recap__back{ font-family:var(--font-meta); font-weight:700; font-size:.64rem;
  letter-spacing:.08em; text-transform:uppercase; color:var(--mj-gray-400);
  text-decoration:none; margin-right:10px }
.mj-recap__back:hover{ color:var(--mj-ink) }
body.dark-mode .mj-recap__back:hover{ color:#f2f2f2 }
.mj-recap__cta{ display:inline-flex; align-items:center; padding:5px 12px;
  border-radius:999px; background:var(--rb-green); color:#fff;
  font-family:var(--font-display); font-weight:700; font-size:.76rem; text-decoration:none }
.mj-recap__cta:hover{ filter:brightness(1.07) }
.mj-recap__cta:focus-visible,.mj-recap__back:focus-visible{ outline:2px solid var(--rb-green); outline-offset:2px }
.mj-recap__v{ margin:var(--sp-3) 0 0; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:.08em; text-transform:uppercase; color:var(--mj-gray-400) }
@media (max-width:560px){
  .mj-recap__f{ display:none }        /* the fit line is the first thing to go on a phone */
  .mj-recap__t th:nth-child(2){ display:none }
}

/* ── COLOUR GROUPS · `data-group` on the review block ──────────────────────
   Lucky, 2026-08-24: products that sit close together on price, use case and
   audience share a frame colour, so a scanning reader sees the groupings before
   reading a word. **The element DESIGN never changes** — only the frame tint and
   the rank chip. Four card designs would be four designers; one design in four
   tints is a system.
   The palette is deliberately small and semantic, not decorative:
     entry   = the R699 machines you buy outright          → brand green
     scale   = the ~R1,499 printing / high-volume tier      → ink blue
     rental  = you never own it, and it returns             → charcoal
     enterprise = quote-based, a call not a checkout        → brand red
   ------------------------------------------------------------------------- */
.scms-rb--full[data-group="entry"]{ --rb-frame:#0DB980 }
.scms-rb--full[data-group="scale"]{ --rb-frame:#1F5FBF }
.scms-rb--full[data-group="rental"]{ --rb-frame:#3A3A40 }
.scms-rb--full[data-group="enterprise"]{ --rb-frame:#EC2227 }

/* ══════════════════════════════════════════════════════════════════════════
   THE SHORT ANSWER AS A CONVERSION SURFACE — design request 2026-08-28
   ══════════════════════════════════════════════════════════════════════════
   The short answer is the highest-value real estate on the site and it was pure
   text: a reader who had just decided had nowhere to go but down the page or off
   it. Meanwhile the body of every article is losing inserts and highlights
   because they read as chaos. The conversion weight has to live somewhere, and
   the honest place is the moment the reader is already deciding.

   THE CONSTRAINT: a click never fires the reader out of the article — that reads
   as a sale. It opens something in place, opt-in, which is what lets it be
   commercial without being pushy.

   The trigger treatment is `.mj-ireveal__t` and it is deliberately reused: a
   dotted INK underline and a caret, already distinct from `mj-hl` (a coloured
   highlight, "this is important") and from a link ("this takes you elsewhere").
   Three meanings, three treatments, one convention a reader learns once. */

/* ── the panel region under the paragraph ─────────────────────────────────
   Empty until a reader asks, so a strip with no triggers renders exactly as it
   did before this element existed. */
.mj-vstrip__panels:empty{ display:none }
/* ── THE PANEL REGION: WHITE, AND FREE TO BREAK OUT ───────────────────────
   Below the answer the elements own the space. They sit on the page ground
   rather than the strip's tint, each carrying its own white surface — which is
   what makes a product box read as an object you can act on rather than as
   more of the paragraph above it.

   `--sa-out` is how far an element may break past the reading column. It is a
   variable so an element can ask for more (a five-column strip, a wide table)
   without every element being wide by default, and it collapses to zero on a
   narrow screen where there is nothing to break out of. */
.mj-vstrip__panels{ margin:var(--sp-5) 0 0; background:none }

/* ── FIT FIRST. BREAK OUT ONLY WHEN THERE IS SOMETHING TO BREAK OUT FOR ────
   THE RULE, and it applies to every insert in every vertical:

     An element takes the ARTICLE'S OWN WIDTH by default.
     It may go wider only when it carries THREE OR MORE things.

   Lucky: "if you have two product box inserts they should fit into the article
   content width. For them to branch out, that will only be if we have three or
   more. Most of the time smaller and compact is better for a quick grasp." He
   is right, and the failure was mine: I made breaking out the default because
   the CAPABILITY was the thing I had just built, which is a bad reason for a
   default. Two cards stretched across 1000px are two cards a reader's eye has
   to travel between; the same two at 680px are a comparison.

   The same rule covers tables: three columns fit the column, and only a table
   that genuinely cannot be read at 680px asks for more. `is-wide` is set by the
   renderer from the CONTENT — never authored, so nobody has to remember it. */
.mj-vstrip__panel{ --sa-out:0px;
  margin-left:calc(var(--sa-out) * -1); margin-right:calc(var(--sa-out) * -1) }
/* FROM 768px, AS FAR AS THE PAGE ALLOWS (Lucky, c-je4y 1 + c-9xek, 2026-09-12:
   "widen it a little bit so that the first 3 can always show in full by
   default"). The panel only widened above 1100px, so on a 1017px laptop and in
   the Studio preview the rail had a 630px track and showed two cards of five.
   The reach is now the page's own spare room, (100vw − 680px column − 48px of
   gutters) ÷ 2, capped at 88px: at 768px that is 20px a side, enough for three
   214px cards; from about 904px it is the full 88px. */
/* AS WIDE AS THREE CARDS NEED, AND NO WIDER (Lucky, 2026-09-12): "the default width
   of the product box element that shows best recommended should be up to when the
   first 3 are shown properly and without a cut out. It shouldn't be as wide as other
   wide inserts." 40px a side on the 680px column gives the rail ~710px inside the
   panel: three cards of ~228px and two gaps, exactly. */
@media(min-width:768px){ .mj-vstrip__panel.is-wide{ --sa-out:clamp(0px, calc((100vw - 728px) / 2), 40px) } }
/* (the 150px reach at 1340px is gone: the rail never needs more than three cards' width) */
.mj-vstrip__panel[hidden]{ display:none }
/* MARGIN-TOP ONLY (2026-09-12). This rule set `margin:… 0 0` AFTER the rule above
   that sets the panel's side margins from `--sa-out`, so it reset them to zero and
   no short-answer panel has ever broken out, at any width. Measured: --sa-out 88px,
   margins 0px, a 630px track, two of five rail cards visible. */
.mj-vstrip__panel{ margin-top:var(--sp-3); animation:mj-sa-in .16s ease-out }

/* ── A PANEL THAT IS ALREADY OPEN ─────────────────────────────────────────
   The short answer's panels all started closed, and that is backwards for the
   main one: a reader who has to press a word to see the comparison mostly does
   not press it, so the element carrying the actual decision was the one nobody
   saw. Open is the default now and reveal is the exception, kept for the
   secondary option most readers do not want.

   AN OPEN PANEL NEEDS A NAME, because it no longer has a trigger phrase to
   introduce it. The label is the writer's own words — the phrase that used to
   be the button — set in the meta face so it reads as a caption and not as a
   second headline competing with the answer above it. */
.mj-vstrip__panel.is-open{ animation:none }
/* EACH ELEMENT ON ITS OWN WHITE SURFACE. Lucky: "just make sure that you give
   each one of these product boxes a white border like you have done on Layout 5
   … the idea here is to just have things feel organized and well prepared."
   The label stays on the page ground — it is a caption, not part of the object
   — so the white starts where the element does. */
.mj-vstrip__panel-b{ --pb-pad:var(--sp-5); display:block; background:var(--mj-surface);
  border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  padding:var(--pb-pad); box-shadow:0 1px 2px rgba(10,10,10,.04) }
body.dark-mode .mj-vstrip__panel-b{ background:#141416; border-color:#2a2a2d;
  box-shadow:none }
/* THE LABEL SITS INSIDE THE WHITE, WITH THE THING IT NAMES.
   It was on the page ground, left-aligned to the panel region — which, once the
   region could break out, put it further left than the sentence it belonged
   under, hanging off the side of the strip. Lucky: "that text feels a little
   bit off because it's falling outside the width of the short answer element…
   consider adding it to the white part." It is the element's own caption; it
   belongs on the element. */
.mj-vstrip__panel-k{ margin:0 0 var(--sp-4) }
@media(max-width:700px){ .mj-vstrip__panel-b{ padding:var(--sp-4) } }
/* LUCKY, c-vgzr 5: "boost the appearance of the THE FIVE WORTH A LOOK FIRST
   text." It was grey 9.6px — the quietest thing in a panel it is the title of.
   It is now the panel's masthead: ink, wider tracking, and the house's red rule
   in front of it, which is the same mark the section kickers use. Weight, not
   size: a caption that competes with the product names would be worse. */
.mj-vstrip__panel-k{ display:flex; align-items:center; gap:9px;
  font-family:var(--font-meta); font-size:.68rem;
  letter-spacing:1.7px; text-transform:uppercase; font-weight:800;
  color:var(--mj-black); margin:0 0 var(--sp-3) }
.mj-vstrip__panel-k::before{ content:""; flex:none; width:20px; height:3px;
  border-radius:2px; background:var(--mj-red) }
body.dark-mode .mj-vstrip__panel-k{ color:#f2f2f5 }

/* The sentence that offers the extras, between the open panel and the reveal.
   Quieter than the answer, because it is an aside about a variant. */
.mj-vstrip__more{ margin:var(--sp-4) 0 0; font-size:.86rem; line-height:1.5;
  color:var(--mj-gray-400) }
body.dark-mode .mj-vstrip__more{ color:#9a9aa2 }

/* ── ROOM TO BREATHE, AND NO SECOND FRAME ─────────────────────────────────
   The strip already carries a red rule down its left edge, which is the
   element's whole identity. Anything inside it that draws its own heavy border
   is a frame inside a frame, and on a table that is what made the short answer
   feel cramped. Lucky: "the border style is a little bit too much, considering
   that we already have the red border to the left."

   So a table inside the strip loses its outer frame and keeps its rules — the
   grid a table actually needs — and gains the width the strip's padding was
   taking from it. */
.mj-vstrip__panel .mj-tw{ border:0; padding:0; background:none; box-shadow:none }
.mj-vstrip__panel .mj-tw__head{ padding-left:0; padding-right:0 }
/* THE TABLE IS A FULL-BLEED REGION OF THE CARD, PULLED OUT ONCE.
   The previous version left `.mj-fct` inset by the panel's padding and pulled
   each child out of it with a negative margin — the scroll wrapper, the
   deciding footer, and the answered band. All three then sat OUTSIDE
   `.mj-fct`'s own `overflow:hidden` and were clipped straight back to the
   inset box: measured, the clip ran at x=220 while the answered band started
   at x=196, which cut the first character off every line of the
   recommendation ("Your match" rendered as "our match"). The table only
   looked correct because its cell padding happened to clear the clip edge.
   So the CONTAINER is pulled to the card's edges once, and the things inside
   it pad themselves back in. One rule owns the bleed; nothing fights it. */
.mj-vstrip__panel .mj-fct{ border:0; background:none; padding:0;
  margin-inline:calc(var(--pb-pad, var(--sp-5)) * -1);
  margin-bottom:calc(var(--pb-pad, var(--sp-5)) * -1);
  border-radius:0 0 var(--radius-md) var(--radius-md) }
.mj-vstrip__panel table{ width:100% }

/* THE PAIR STRIP LOSES ITS FRAME HERE TOO, and gains a divider instead.
   Inside the strip it was a bordered card holding two more cards, each with its
   own red rule — four frames deep, inside an element whose identity is already
   a red rule down the left. The cards still need separating from each other,
   which is a job for one line between them rather than a box around each. */
.mj-vstrip__panel .mj-pstrip{ border:0; background:none; padding:0; border-radius:0 }
/* The cards carry their own frame now (see .mj-pstrip__card), so the divider
   that used to separate two frameless columns would be a third line between two
   bordered things. The gap does that job. */
.mj-vstrip__panel .mj-pstrip__cards{ gap:var(--sp-4) }

/* ── AND ROOM AFTER IT ────────────────────────────────────────────────────
   An element that ends and a paragraph that starts immediately under it read
   as one block, so the reader carries the element's frame into the sentence.
   Lucky: "there must be enough space between the paragraph and where these
   inserts are used." The gap belongs to the ELEMENT, not to the paragraph, so
   it is set here once rather than on every element that might follow one. */
.mj-article > .mj-pstrip,
.mj-article > .mj-fct,
.mj-article > .mj-pb,
.mj-article > .mj-sb,
.mj-article > .mj-tw{ margin-bottom:var(--sp-7) }
.mj-vstrip + p, .mj-pstrip + p, .mj-fct + p, .mj-tw + p{ margin-top:var(--sp-6) }
@keyframes mj-sa-in{ from{ opacity:0; transform:translateY(-2px) } to{ opacity:1; transform:none } }
@media (prefers-reduced-motion:reduce){ .mj-vstrip__panel{ animation:none } }

/* ── PICK STRIP · one product, wide and shallow ───────────────────────────
   Shallow ON PURPOSE. These panels open inside a paragraph's flow, and a tall
   panel pushes the reader's own place off the screen — the opposite of "opens
   in place". No scores, no axes, no pros and cons: the argument already
   happened in the prose directly above it. */
.mj-pick{ display:grid; grid-template-columns:minmax(0,1fr) auto; gap:var(--sp-4);
  align-items:center; padding:var(--sp-4) var(--sp-5);
  border:1px solid var(--mj-rule); border-left:3px solid var(--rb-green);
  border-radius:var(--radius-md); background:var(--mj-surface) }
body.dark-mode .mj-pick{ background:#141416; border-color:#2a2a2d;
  border-left-color:var(--rb-green) }
.mj-pick__eyebrow{ display:block; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-gray-400) }
.mj-pick__name{ font-family:var(--font-display); font-weight:800; font-size:1.08rem;
  margin:2px 0 4px; color:var(--mj-black) }
body.dark-mode .mj-pick__name{ color:#fff }
.mj-pick__why{ margin:0; font-size:.9rem; line-height:1.5; color:var(--mj-text-muted) }
body.dark-mode .mj-pick__why{ color:#a8a8ae }
.mj-pick__money{ display:flex; flex-direction:column; gap:8px; align-items:flex-end }
.mj-pick__ladder{ display:flex; align-items:baseline; gap:8px; margin:0; white-space:nowrap }
.mj-pick__was{ font-family:var(--font-display); font-weight:700; font-size:.92rem;
  color:var(--mj-struck); text-decoration:line-through }
.mj-pick__now{ font-family:var(--font-display); font-weight:800; font-size:1.32rem;
  color:var(--mj-black); letter-spacing:-.01em }
body.dark-mode .mj-pick__now{ color:#fff }
.mj-pick__tag{ font-family:var(--font-meta); font-size:.6rem; font-weight:700;
  letter-spacing:1px; text-transform:uppercase; white-space:nowrap;
  color:var(--mj-growth-text); background:var(--mj-growth-subtle);
  border:1px solid rgba(5,122,83,.24); border-radius:var(--radius-sm); padding:4px 9px }
body.dark-mode .mj-pick__tag{ background:rgba(62,224,166,.16); color:#3ee0a6;
  border-color:rgba(62,224,166,.32) }
.mj-pick__cta{ display:inline-flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; text-transform:uppercase;
  font-size:.76rem; letter-spacing:.3px; padding:11px 18px; white-space:nowrap;
  background:var(--link-action); color:#fff; text-decoration:none;
  border-radius:var(--radius-sm); transition:background .18s ease }
.mj-pick__cta:hover{ background:var(--mj-red-hover) }
/* The pick strip is a narrow money column, so the shared ladder stacks rather
   than laying out in a row. Same grid, same tokens, different host. */
.mj-pick .mj-pb__pay{ background:none; padding:0; margin:0 0 var(--sp-3);
  gap:var(--sp-2) var(--sp-3) }
.mj-pick__stamp{ grid-column:1 / -1; margin:0; font-family:var(--font-meta);
  font-size:.62rem; letter-spacing:.3px; color:var(--mj-gray-400) }
@media(max-width:560px){
  .mj-pick{ grid-template-columns:minmax(0,1fr); padding:var(--sp-4) }
  .mj-pick__money{ align-items:stretch }
  .mj-pick__ladder{ justify-content:flex-start }
  .mj-pick__cta{ width:100% }
}

/* ── PAIR STRIP · two products sharing a row ──────────────────────────────
   NOT a comparison table, and the distinction matters: a table invites a
   reader to weigh columns, which is the job the prose already did. This is two
   product boxes sharing a row, each with its own action, plus one line naming
   the step-up so the reader does not have to go looking for it. */
/* ── THE RAIL · pick_rail (2026-09-11, Lucky) ─────────────────────────────
   "create another version of the product box inserts for short answer
   sections … it can list more than two products. But it is not that tall and
   it should also have a slider." The list articles rank five, seven, eleven
   machines and their short answer showed two, so three brands a reader could
   buy were invisible at the moment most readers decide.

   SHALLOW IS THE BRIEF. A pair-strip card runs shot / badge / name / why /
   two-column ladder / note / button, which is right for two and far too tall
   for a row. Here the shot shrinks to 56px beside the name, the reason is
   clamped to three lines, and the price is ONE line — what you pay, loud,
   with the vendor's price struck beside it only when something comes off. The
   saving still wears `.mj-pb__pay-tag`, the one chip the desk uses for it.

   THE CUT CARD IS THE AFFORDANCE. Three cards fit a desktop panel and the
   fourth is sliced by the edge, which says "this moves" before any button
   does. The track is a native scroller with snap points, so a thumb, a
   trackpad and the arrow keys all work with no script at all; the script only
   adds the two buttons and the count. */
.mj-prail{ --rail-card:clamp(214px, 31%, 252px); --rail-gap:var(--sp-3) }
/* THE CARDS DIVIDE THE RAIL IN THREE (2026-09-12). A container query, so a card is
   exactly a third of the rail wherever the rail is, and the third card is never
   shown as half. Below three 214px cards (a phone) they keep their minimum and the
   row scrolls, as it always did. */
.mj-vstrip__panel .mj-prail{ container-type:inline-size;
  --rail-card:max(214px, calc((100cqi - 2 * var(--rail-gap)) / 3)) }
.mj-vstrip__panel .mj-prail__track{ padding-inline:0; scroll-padding-inline:0;
  -webkit-mask-image:none !important; mask-image:none !important }
.mj-prail__top{ display:flex; align-items:center; justify-content:space-between;
  gap:var(--sp-3); margin:0 0 var(--sp-3) }
.mj-prail__lead{ margin:0; font-size:.86rem; line-height:1.45; color:var(--mj-text-muted) }
body.dark-mode .mj-prail__lead{ color:#a8a8ae }
.mj-prail__nav{ display:none; align-items:center; gap:6px; margin-left:auto; flex:none }
.mj-prail.is-live .mj-prail__nav{ display:flex }
.mj-prail.is-live.is-static .mj-prail__nav{ display:none }
.mj-prail__count{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-gray-400); margin-right:4px; white-space:nowrap }
.mj-prail__btn{ width:32px; height:32px; display:inline-flex; align-items:center;
  justify-content:center; padding:0; font-size:1.25rem; line-height:1; cursor:pointer;
  background:var(--mj-surface); color:var(--mj-ink-900); border:1px solid var(--mj-rule);
  border-radius:999px; transition:background .15s, border-color .15s, opacity .15s }
.mj-prail__btn:hover:not(:disabled){ background:var(--mj-ink-900); color:#fff; border-color:var(--mj-ink-900) }
.mj-prail__btn:disabled{ opacity:.35; cursor:default }
.mj-prail__btn:focus-visible, .mj-prail__track:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
body.dark-mode .mj-prail__btn{ background:#1b1b1e; color:#e7e7ea; border-color:#33333a }
body.dark-mode .mj-prail__btn:hover:not(:disabled){ background:#e7e7ea; color:#141416 }

.mj-prail__track{ display:flex; gap:var(--rail-gap); overflow-x:auto; overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory; scroll-padding-inline:2px; padding:2px 2px var(--sp-2);
  scrollbar-width:thin; scrollbar-color:var(--mj-rule) transparent }
.mj-prail__track::-webkit-scrollbar{ height:4px }
.mj-prail__track::-webkit-scrollbar-thumb{ background:var(--mj-rule); border-radius:4px }
/* THE EDGE FADES ONLY WHERE THERE IS MORE. A fade on a row that has reached
   its end would promise a card that is not there. */
.mj-prail.is-live:not(.at-end):not(.is-static) .mj-prail__track{
  -webkit-mask-image:linear-gradient(90deg,#000 88%,transparent);
  mask-image:linear-gradient(90deg,#000 88%,transparent) }

.mj-prail__card{ flex:0 0 var(--rail-card); scroll-snap-align:start; min-width:0;
  display:flex; flex-direction:column; gap:var(--sp-2);
  padding:var(--sp-3) var(--sp-4) var(--sp-4); background:var(--mj-surface);
  border:1px solid var(--mj-rule); border-radius:var(--radius-md) }
/* THE PICK IS MARKED THE WAY THE STRIP IS: a red rule, on top this time
   because the strip's own rule already owns the left edge. */
.mj-prail__card[data-pick="1"]{ box-shadow:inset 0 3px 0 var(--mj-red) }
body.dark-mode .mj-prail__card{ background:#141416; border-color:#2a2a2d }

.mj-prail__head{ display:grid; grid-template-columns:56px minmax(0,1fr); gap:var(--sp-3);
  align-items:center; min-height:56px }
.mj-prail__head:not(:has(.mj-prail__shot)){ grid-template-columns:minmax(0,1fr) }
.mj-prail__shot{ width:56px; height:56px; display:flex; align-items:center; justify-content:center;
  background:var(--mj-surface-2); border-radius:var(--radius-sm) }
body.dark-mode .mj-prail__shot{ background:#1b1b1e }
.mj-prail__shot img{ max-width:46px; max-height:46px; width:auto; height:auto; object-fit:contain }
/* A VENDOR'S MARK IN THE SHOT (c-ulg9). A wordmark squeezed into a 46px square
   is a smudge, so a logo gets a wider, shorter frame and the head's first
   column follows it. */
/* ONE WIDTH FOR EVERY MARK. `width:auto` let each logo size itself, and five cards
   measured 96, 56, 56 and 56px: the row read as uneven before a word was read. */
.mj-prail__shot[data-kind="logo"]{ width:88px; padding:0 8px }
.mj-prail__shot[data-kind="logo"] img{ max-width:80px; max-height:28px }
.mj-prail__head:has(.mj-prail__shot[data-kind="logo"]){ grid-template-columns:auto minmax(0,1fr) }
.mj-prail__id{ min-width:0; display:flex; flex-direction:column; align-items:flex-start; gap:4px }
.mj-prail__rank{ font-family:var(--font-meta); font-size:.58rem; font-weight:700; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-gray-400) }
/* Same badge language as the pair strip: rounded, blue for the pick, ink for
   the alternatives — so a reason to buy never looks like a price fact. */
.mj-prail__badge{ margin:0; max-width:100%; font-family:var(--font-meta); font-size:.55rem;
  font-weight:700; letter-spacing:1px; text-transform:uppercase; line-height:1.3;
  color:var(--mj-trust); background:var(--mj-trust-subtle); border:1px solid rgba(37,99,235,.28);
  border-radius:999px; padding:3px 8px }
.mj-prail__badge[data-tone="alt"]{ color:var(--mj-black); background:var(--mj-surface-2); border-color:var(--mj-rule) }
body.dark-mode .mj-prail__badge{ background:rgba(37,99,235,.16); color:#8fb4ff; border-color:rgba(37,99,235,.34) }
body.dark-mode .mj-prail__badge[data-tone="alt"]{ color:#e6e6ea; background:#1f1f23; border-color:#33333a }
.mj-prail__name{ margin:0; font-family:var(--font-display); font-weight:800; font-size:.95rem;
  line-height:1.2; color:var(--mj-black) }
body.dark-mode .mj-prail__name{ color:#fff }
.mj-prail__why{ margin:0; font-size:.82rem; line-height:1.45; color:var(--mj-text-muted);
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden }
body.dark-mode .mj-prail__why{ color:#a8a8ae }

.mj-prail__buy{ margin-top:auto; padding-top:var(--sp-2); border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-prail__buy{ border-top-color:#2a2a2d }
.mj-prail__price{ display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 8px; margin:0 0 var(--sp-2) }
.mj-prail__now{ font-family:var(--font-display); font-weight:800; font-size:1.2rem; line-height:1;
  color:var(--mj-black) }
body.dark-mode .mj-prail__now{ color:#fff }
.mj-prail__was{ font-family:var(--font-display); font-size:.84rem; color:var(--mj-gray-400) }
.mj-prail__price .mj-pb__pay-tag{ align-self:center }
.mj-prail__unit{ flex-basis:100%; font-family:var(--font-meta); font-size:.6rem; letter-spacing:.4px;
  color:var(--mj-gray-400) }
.mj-prail__cta{ display:flex; align-items:center; justify-content:center; width:100%;
  font-family:var(--font-display); font-weight:800; text-transform:uppercase; font-size:.68rem;
  letter-spacing:.3px; padding:8px 12px; text-decoration:none; border-radius:var(--radius-sm);
  border:1px solid var(--mj-ink-900); color:var(--mj-ink-900); background:transparent; transition:.16s }
.mj-prail__cta:hover{ background:var(--mj-ink-900); color:#fff }
.mj-prail__card[data-pick="1"] .mj-prail__cta{ background:var(--mj-red); border-color:var(--mj-red); color:#fff }
.mj-prail__card[data-pick="1"] .mj-prail__cta:hover{ background:var(--mj-ink-900); border-color:var(--mj-ink-900) }
body.dark-mode .mj-prail__cta{ border-color:#4a4a52; color:#e7e7ea }
body.dark-mode .mj-prail__cta:hover{ background:#e7e7ea; color:#141416 }
.mj-prail__disclosure{ margin:5px 0 0; font-size:.62rem; line-height:1.35; color:var(--mj-gray-400) }
/* ── THE RAIL, SECOND PASS (Lucky, c-vgzr, 2026-09-12) ──────────────────────
   Six of his seven points are here; the seventh is the note, below.

   4. "The nav buttons are not easy to see, also add some appropriate
      mashjoy.com colour (maybe red)… the nav icons are not well centered and
      the 1-2 of 5 text seems to not be well aligned with the buttons."
      The buttons were a white circle with a 1px grey rule, which on a white
      panel is nearly nothing. They are now filled red — the house's action
      colour, the same one the pick's button wears — and the count sits in a
      box the same height as they are, so the three things share one centre
      line instead of three baselines. */
.mj-prail__nav{ gap:7px }
.mj-prail__btn{ width:34px; height:34px; background:var(--mj-red); color:#fff;
  border-color:var(--mj-red); box-shadow:0 1px 2px rgba(0,0,0,.12) }
.mj-prail__btn svg{ display:block; width:17px; height:17px; fill:none;
  stroke:currentColor; stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round }
.mj-prail__btn:hover:not(:disabled){ background:var(--mj-ink-900); border-color:var(--mj-ink-900); color:#fff }
.mj-prail__btn:disabled{ opacity:.24; box-shadow:none }
.mj-prail__count{ display:inline-flex; align-items:center; height:34px; line-height:1;
  font-size:.64rem; letter-spacing:1.2px; font-weight:700; color:var(--mj-ink-900);
  margin-right:6px }
body.dark-mode .mj-prail__btn{ background:var(--mj-red); border-color:var(--mj-red); color:#fff }
body.dark-mode .mj-prail__btn:hover:not(:disabled){ background:#e7e7ea; color:#141416; border-color:#e7e7ea }
body.dark-mode .mj-prail__count{ color:#e7e7ea }

/* 2 + 3. "You have used the mashjoy.com blue for best overall badge, which is
   good, consider also colour coding the other ones with some brand colour…
   the buttons in other product boxes are stale."
   The pick keeps the house colours because the verdict is ours. Every other
   card carries the vendor's own, from site.json `brand_colors`, with the ink
   computed against it — iKhokha's yellow needs black type and adumo's purple
   needs white, and CSS cannot measure that (see renderers._brand_style). */
/* A TINT, NOT A FILL. The pick's badge is blue type on a pale blue ground with
   a blue rule — that is the house's badge language, and a saturated block of
   iKhokha yellow in the same slot read as a warning label rather than a reason.
   The brand hue is mixed toward white for the ground and toward black for the
   type, so any vendor colour lands readable at 8px. The badge also sits on the
   card's full width now: in the narrow column beside the logo, "Best for
   covering its costs" wrapped to two lines and pushed the product name down. */
.mj-prail__badge{ align-self:flex-start; max-width:100% }
.mj-prail__card[style*="--brand-accent"] .mj-prail__badge[data-tone="alt"]{
  color:color-mix(in srgb, var(--brand-accent) 64%, #101014);
  background:color-mix(in srgb, var(--brand-accent) 13%, #ffffff);
  border-color:color-mix(in srgb, var(--brand-accent) 32%, #ffffff) }
body.dark-mode .mj-prail__card[style*="--brand-accent"] .mj-prail__badge[data-tone="alt"]{
  color:color-mix(in srgb, var(--brand-accent) 72%, #ffffff);
  background:color-mix(in srgb, var(--brand-accent) 20%, #141416);
  border-color:color-mix(in srgb, var(--brand-accent) 34%, #141416) }
.mj-prail__card[style*="--brand-accent"] .mj-prail__cta,
body.dark-mode .mj-prail__card[style*="--brand-accent"] .mj-prail__cta{
  background:var(--brand-accent); border-color:var(--brand-accent); color:var(--brand-ink) }
.mj-prail__card[style*="--brand-accent"] .mj-prail__cta:hover,
body.dark-mode .mj-prail__card[style*="--brand-accent"] .mj-prail__cta:hover{
  background:var(--mj-ink-900); border-color:var(--mj-ink-900); color:#fff }
/* The brand's colour also tops the card, the way the pick's red does — so a
   scanning eye can tell two vendors apart before reading either name. */
.mj-prail__card[style*="--brand-accent"]{ box-shadow:inset 0 3px 0 var(--brand-accent) }

/* 1. "Consider conditionally making the best for text to have a slightly
   smaller text if it is a bit long so that the design can feel consistent."
   Set by the renderer at 22 characters, which is where "Best for covering its
   costs" starts taking a second line while "Best overall" has one word. */
.mj-prail__badge[data-fit="long"]{ font-size:.5rem; letter-spacing:.8px; padding:3px 7px }

/* 7. "The once off text is sometimes hanging far from the price… when price
   and the +R100 OFF need a bit more space then it should be made slightly
   smaller for best fit."  The unit note used to be forced onto its own line
   (flex-basis:100%), which is what pushed "once-off" away from the number it
   qualifies. It now rides beside the price, and the whole line steps down when
   the renderer measures it past 20 characters. */
.mj-prail__unit{ flex-basis:auto }
.mj-prail__price[data-fit="tight"] .mj-prail__now{ font-size:1.02rem }
.mj-prail__price[data-fit="tight"] .mj-prail__was{ font-size:.76rem }
.mj-prail__price[data-fit="tight"] .mj-prail__unit{ font-size:.56rem }
.mj-prail__price[data-fit="tight"] .mj-pb__pay-tag{ font-size:.52rem; padding:2px 6px }

/* 6. "Remove the may earn commission at no cost to you text… replace with
   helpful extra information for sale."  Two kinds of line live here now: what
   comes off the reader's price (a deal, marked with the house's saving green)
   and where the button lands (a route, quiet). Neither mentions us. */
.mj-prail__note{ margin:6px 0 0; font-size:.66rem; line-height:1.4; color:var(--mj-text-muted) }
.mj-prail__note[data-kind="deal"]{ display:flex; gap:6px; color:var(--mj-black); font-weight:600 }
/* the green dot became the sale tag — see ICON SYSTEM below */
body.dark-mode .mj-prail__note{ color:#9a9aa2 }
body.dark-mode .mj-prail__note[data-kind="deal"]{ color:#e7e7ea }

.mj-prail__stamp{ margin:var(--sp-2) 0 0; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:.5px; color:var(--mj-gray-400) }
@media(max-width:600px){
  .mj-prail{ --rail-card:78% }
  .mj-prail__top{ flex-wrap:wrap }
}
@media(prefers-reduced-motion:reduce){ .mj-prail__track{ scroll-behavior:auto } }

.mj-pstrip{ border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  background:var(--mj-surface); padding:var(--sp-4) var(--sp-5) var(--sp-3) }
body.dark-mode .mj-pstrip{ background:#141416; border-color:#2a2a2d }
/* WHAT THIS SET IS. A strip following another strip with no line between them
   reads as one long run of products, and the reader has to work out from the
   names that the second pair is the printing versions of the first. */
.mj-pstrip__lead{ margin:0 0 var(--sp-4); font-size:.92rem; line-height:1.55;
  color:var(--mj-text-muted) }
/* TWO CARDS SIDE BY SIDE HAVE TO LINE UP ROW FOR ROW.
   Each card was its own flex column, so every part sat wherever the part above
   it happened to end: a one-line reason in the left card against a two-line
   reason in the right put the two price ladders 20px out of step and the two
   buttons 32px apart. A pair strip exists so a reader can compare ACROSS —
   and a comparison you have to re-find on each side is the one thing it must
   not be. The cards share the strip's rows through `subgrid`, so the shots,
   the reasons, the ladders and the buttons each start on the same line no
   matter how long any one card's text runs.
   The rows are placed EXPLICITLY rather than by order, because a card may
   legitimately have no pay-note — and with auto-placement that card's footer
   would slide up into the note's row and undo the alignment for the sake of
   the one part that most needs it: the button. */
.mj-pstrip__cards{ display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-4);
  grid-template-rows:auto auto auto auto auto }
/* TWO CARDS, BUILT LIKE THE FIVE (Lucky, 2026-09-16).
   "Improve it so that it can take on everything we have learned … so they look
   more like the ones where we have the five worth a look. This one feels stale."
   The pair strip was still the oldest shape in the set: a rule down the left of
   each card and nothing else, while the rail (Panel C) had moved to a real card
   — a surface, a frame, a vendor mark and a button that fills its width. Same
   card here, in two columns instead of five, so a reader meets ONE product card
   on this site rather than two that happen to disagree. */
.mj-pstrip__card{ display:grid; grid-template-rows:subgrid; grid-row:span 5;
  align-content:start; min-width:0; gap:var(--sp-2);
  padding:var(--sp-3) var(--sp-4) var(--sp-4);
  background:var(--mj-surface); border:1px solid var(--mj-rule);
  border-radius:var(--radius-md) }
body.dark-mode .mj-pstrip__card{ background:#141416; border-color:#2a2a2d }
/* The recommended one is marked the way the rail marks it: a rule on top, where
   it cannot be mistaken for the red LEFT rule that means "short answer". */
.mj-pstrip__card.is-pick{ box-shadow:inset 0 3px 0 var(--mj-red) }
.mj-pstrip__card > .mj-pstrip__head{ grid-row:1 }
.mj-pstrip__card > .mj-pstrip__why{ grid-row:2 }
.mj-pstrip__card > .mj-pb__pay{ grid-row:3 }
.mj-pstrip__card > .mj-pstrip__paynote{ grid-row:4 }
.mj-pstrip__card > .mj-pstrip__foot{ grid-row:5; align-self:end }
/* `subgrid` is the whole point, so a browser without it must still get two
   readable cards rather than five stacked rows. */
@supports not (grid-template-rows:subgrid){
  .mj-pstrip__card{ display:flex; flex-direction:column; gap:5px }
}

/* ── THE PRODUCT SHOT ─────────────────────────────────────────────────────
   A purchase decision the reader cannot see is a purchase decision made on
   trust in our prose alone. Contained rather than cropped — a terminal is a
   tall object and `cover` would cut the card reader off it — on the surface
   tint so a transparent PNG does not float. */
/* ── THE HEAD: A SQUARE SHOT, AND THE IDENTITY BESIDE IT ──────────────────
   Stacked, the card ran image / badge / name / why / ladder / note / button and
   got tall enough that two of them side by side pushed both buttons below the
   fold. The shot is a fixed square on the left and the two things that identify
   the product sit in the width that frees up. */
/* ALIGNED ON THE NAME, NOT ON THE TOP. Two badges of different lengths — "Best
   on fees" and "Best if the counter earns" — are one line and two, so centring
   the column put one card's product name a line below the other's and the pair
   read as misaligned. The name is what the eye pairs across the two cards, so
   the column is anchored at its BOTTOM and the badge grows upward from it. */
.mj-pstrip__head{ display:grid; grid-template-columns:76px 1fr; gap:var(--sp-3);
  align-items:end; margin:0 0 var(--sp-2); min-height:76px }
/* A CARD WITH NO SHOT MUST NOT KEEP THE SHOT'S COLUMN.
   The figure is dropped by `wrap_if` wherever a product has no picture — and
   this grid still reserved 76px for it, so the FIRST remaining child fell into
   that column. The product name then wrapped at 76px, one or two words per
   line, while the sentence beneath it ran the full width of the card: the exact
   thing Lucky reported on the salon and Capitec articles, where "Yoco Khumo 2,
   on the free Core plan" came down the page in a narrow ribbon.
   The column is only reserved when there is something to put in it. A pair
   strip is also used for two CONDITIONS rather than two products — "Under about
   R11 000 a month" — and those have no picture by right, not by accident, so
   this is a permanent state to support and not a fallback to apologise for. */
.mj-pstrip__head:not(:has(.mj-pstrip__shot)){
  grid-template-columns:minmax(0,1fr); min-height:0 }
.mj-pstrip__id{ min-width:0; display:flex; flex-direction:column;
  align-items:flex-start; justify-content:flex-end; gap:6px }
.mj-pstrip__badge{ max-width:100% }
.mj-pstrip__shot{ display:flex; align-items:center; justify-content:center;
  width:76px; height:76px; margin:0; background:var(--mj-surface-2);
  border-radius:var(--radius-sm) }
body.dark-mode .mj-pstrip__shot{ background:#1b1b1e }
/* NO `mix-blend-mode`. It was here to melt a white product background into the
   tint, and it does that — for images that HAVE a white background. The iKhokha
   Flyer Lite shot is a dark photograph, and multiply rendered it as a solid
   black square: a blend mode is a bet about every asset it will ever be given,
   and the harvest is not that uniform. Contained on a neutral tint works for
   both, and is what the product boxes already do. */
.mj-pstrip__shot img{ max-height:62px; max-width:62px; width:auto;
  object-fit:contain }

/* ── THE PRICE LADDER, ALIGNED ────────────────────────────────────────────
   Three prices on one line — what it was, what the vendor asks, what you pay —
   plus a discount chip, all wrapping wherever the column happened to end.
   Lucky: "align the pricing of these products, and make the discount stand
   out." So the ladder is a grid: the struck prices sit together on one row,
   the figure you actually pay owns the line below it at display size, and the
   saving is a chip beside it rather than a fourth grey span nobody reads. */
/* The bespoke ladder is gone too — `.mj-pb__pay` is the one this desk has, and
   it aligns as a grid rather than on a baseline, which is what fixes "nothing
   is a little bit above or below". Inside a card it needs no tinted panel of
   its own; the card already is one. */
.mj-pstrip__card .mj-pb__pay{ background:none; padding:var(--sp-3) 0;
  margin:var(--sp-3) 0 var(--sp-2); gap:var(--sp-2);
  border-top:1px solid var(--mj-rule); border-bottom:1px solid var(--mj-rule) }
/* WHEN THE TAG WRAPS, IT WRAPS TO THE LEFT. A card column is narrow enough
   that the saving drops to a second row, and `flex-wrap` left it sitting
   against the right edge — floating away from the price it belongs to, which
   is the one thing it has to be read WITH. */
.mj-pstrip__card .mj-pb__pay-tag{ margin-right:auto }
body.dark-mode .mj-pstrip__card .mj-pb__pay{ border-color:#2a2a2d }

/* ── WHY THIS ONE ─────────────────────────────────────────────────────────
   The reason a product is here, in the article's own terms. Small, above the
   name, in the meta face: it is a label on the object, not a claim competing
   with the product's name. */
/* ── WHY THIS ONE — AND WHICH "WHY" IT IS ─────────────────────────────────
   Rounded and carrying a mark, the way the review block labels a fit, rather
   than square: square is this desk's language for a PRICE FACT (the saving
   chip), and a reason to buy is not a price fact. Reusing the square there
   would have said the two are the same kind of thing.

   TWO REASONS, TWO COLOURS. Lucky: "because you're showing two different
   reasons for buying, apply different colours so people can easily distinguish
   between the two." The tone is not decoration — it follows the ROLE. The pick
   wears trust blue, which is this desk's colour for a considered position; the
   alternative wears a neutral ink, because it is a real option and not a
   runner-up being scolded. Green is deliberately not used: green means MONEY
   here, and a reason to buy is not a saving. */
/* "BEST IF THE COUNTER EARNS" HAS TO FIT. Lucky: "on the iKhokha iK Flyer Lite
   showcase the 'best if the counter earns' text is not fitted well." A pill that
   cannot wrap makes every badge a bet that the words are short; the rail's badge
   has always wrapped, and this is the rail's badge now. */
.mj-pstrip__badge{ align-self:flex-start; margin:0; white-space:normal;
  line-height:1.3; text-align:left;
  display:inline-flex; align-items:center; gap:5px;
  font-family:var(--font-meta); font-size:.58rem; font-weight:700;
  letter-spacing:1.1px; text-transform:uppercase;
  color:var(--mj-trust); background:var(--mj-trust-subtle);
  border:1px solid rgba(37,99,235,.28); border-radius:999px;
  padding:4px 10px 4px 8px }
.mj-pstrip__badge-i{ font-size:.5rem; line-height:1; opacity:.9; flex:0 0 auto }
/* THE VENDOR TINTS ITS OWN BADGE (the rail's rule, §9039 above). The colour
   identifies; it never acts — the button stays ink or red. */
.mj-pstrip__card[style*="--brand-accent"] .mj-pstrip__badge[data-tone="alt"]{
  color:color-mix(in srgb, var(--brand-accent) 64%, #101014);
  background:color-mix(in srgb, var(--brand-accent) 13%, #ffffff);
  border-color:color-mix(in srgb, var(--brand-accent) 32%, #ffffff) }
body.dark-mode .mj-pstrip__card[style*="--brand-accent"] .mj-pstrip__badge[data-tone="alt"]{
  color:color-mix(in srgb, var(--brand-accent) 72%, #ffffff);
  background:color-mix(in srgb, var(--brand-accent) 18%, #141416);
  border-color:color-mix(in srgb, var(--brand-accent) 38%, #141416) }
body.dark-mode .mj-pstrip__badge{ background:rgba(37,99,235,.16); color:#8fb4ff;
  border-color:rgba(37,99,235,.34) }
.mj-pstrip__badge[data-tone="alt"]{ color:var(--mj-black);
  background:var(--mj-surface-2); border-color:var(--mj-rule) }
body.dark-mode .mj-pstrip__badge[data-tone="alt"]{ color:#e6e6ea;
  background:#1f1f23; border-color:#33333a }

/* ── THE FOOT, LIKE LAYOUT 5's ────────────────────────────────────────────
   A full-width button and the disclosure under it, pinned to the bottom of the
   card so two cards of unequal text still line their buttons up. */
.mj-pstrip__foot{ margin-top:auto; padding-top:var(--sp-3) }
.mj-pstrip__paynote{ margin:0 0 var(--sp-2); font-size:.78rem; line-height:1.45;
  color:var(--mj-text-muted) }
.mj-pstrip__disclosure{ margin:6px 0 0; font-size:.66rem; line-height:1.4;
  color:var(--mj-gray-400) }
/* `.mj-pstrip__off` IS GONE, and its removal is the point.
   It was a 999px pill in a hand-picked green — a FIFTH design for the fact that
   `.mj-offer__tag`, `.mj-pb__pay-tag`, `.scms-rb-pay__tag` and
   `.mj-pb__badge--deal` were unified into one rule to stop exactly this. The
   comment on that rule says a pill "is a different language" and that the same
   saving rendering in two colours was the fault being fixed. I read it after
   writing this. The card uses `.mj-pb__pay-tag` now, like everything else. */
.mj-pstrip__name{ font-family:var(--font-display); font-weight:800; font-size:1rem;
  margin:0; color:var(--mj-black) }
body.dark-mode .mj-pstrip__name{ color:#fff }
.mj-pstrip__why{ margin:0; font-size:.86rem; line-height:1.45; color:var(--mj-text-muted) }
body.dark-mode .mj-pstrip__why{ color:#a8a8ae }
.mj-pstrip__price{ display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; margin:2px 0 0 }
.mj-pstrip__now{ font-family:var(--font-display); font-weight:800; font-size:1.15rem;
  color:var(--mj-black) }

/* ── THE PRICE LADDER (2026-08-28, Lucky) ────────────────────────────────────
   "the pricing part should be corrected so that people can understand that they
   are not only going to buy it for R699. They will also be getting an extra 10%
   on top of that." Three steps, and the one the reader pays is the loud one:
   the regular price struck out, the vendor's price today in the middle, and
   YOUR price last and heaviest. */
.mj-pstrip__ladder{ display:flex; align-items:baseline; flex-wrap:wrap; gap:8px;
  margin:0 0 4px }
.mj-pstrip__was{ font-family:var(--font-display); font-size:.9rem;
  color:var(--mj-gray-400); text-decoration:line-through }
.mj-pstrip__vend{ font-family:var(--font-display); font-size:.98rem;
  color:var(--mj-text-muted); text-decoration:line-through }
.mj-pstrip__yours{ font-family:var(--font-display); font-weight:800; font-size:1.5rem;
  color:var(--mj-black); line-height:1 }
body.dark-mode .mj-pstrip__yours{ color:#fff }
.mj-pstrip__off{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1px;
  text-transform:uppercase; font-weight:700; color:#fff; background:var(--mj-growth);
  padding:3px 8px; border-radius:999px; white-space:nowrap }

/* TWO RED BUTTONS SIDE BY SIDE COMPETE, and a reader reads competition as a
   sales page. Lucky was right to have mixed feelings about it. So the card the
   article actually recommends keeps the red; the other one is the same button in
   ink, which reads as the real alternative it is rather than a second pitch. */
.mj-pstrip__card:not(.is-pick) .mj-pstrip__cta{ background:transparent;
  color:var(--mj-black); border:1px solid var(--mj-ink-900) }
body.dark-mode .mj-pstrip__card:not(.is-pick) .mj-pstrip__cta{ color:#fff;
  border-color:#5a5a60 }
.mj-pstrip__card:not(.is-pick) .mj-pstrip__cta:hover{ background:var(--mj-ink-900);
  color:#fff }
.mj-pstrip__card.is-pick{ position:relative }
@media(max-width:560px){
  .mj-pstrip__yours{ font-size:1.3rem }
  .mj-pstrip__ladder{ gap:6px }
}
body.dark-mode .mj-pstrip__now{ color:#fff }
.mj-pstrip__unit{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:.4px;
  color:var(--mj-gray-400) }
.mj-pstrip__cta{ display:flex; align-items:center; justify-content:center;
  width:100%; margin-top:4px;
  font-family:var(--font-display); font-weight:800; text-transform:uppercase;
  font-size:.72rem; letter-spacing:.3px; padding:9px 16px; text-decoration:none;
  border:1px solid var(--mj-ink-900); color:var(--mj-ink-900);
  border-radius:var(--radius-sm); transition:.16s }
.mj-pstrip__cta:hover{ background:var(--mj-ink-900); color:#fff }
body.dark-mode .mj-pstrip__cta{ border-color:#4a4a52; color:#e7e7ea }
body.dark-mode .mj-pstrip__cta:hover{ background:#e7e7ea; color:#141416 }

/* THE PICK'S BUTTON WAS NEVER ACTUALLY RED. The comment above says "the card the
   article actually recommends keeps the red" and the `:not(.is-pick)` rule below
   it duplicated the base outline button exactly, so `is-pick` had no visual
   effect at all and the two cards were indistinguishable. A reader scanning the
   short answer could not see which one we recommend, which is the one job the
   panel has. Written AFTER the base rule on purpose: this file has twice shipped
   a variant that sat above its base declaration. */
/* THE PICK IS NAMED, NOT PAINTED. `is-pick` turned the card's left rule red,
   so the in-article strips carried a red bar down every recommended card —
   which Lucky flagged on the "here is the whole of both ranges" showcase.
   Red is this site's ACTION colour and a stationary bar is not an action. The
   card already says which one it is, in words, in its badge, and the filled
   red button already carries the action. A third signal for the same fact is
   noise. The rule stays neutral. */
.mj-pstrip__card.is-pick .mj-pstrip__cta{ background:var(--mj-red); color:#fff;
  border-color:var(--mj-red) }
.mj-pstrip__card.is-pick .mj-pstrip__cta:hover{ background:var(--mj-red-hover);
  border-color:var(--mj-red-hover); color:#fff }
body.dark-mode .mj-pstrip__card.is-pick .mj-pstrip__cta{ background:var(--mj-red);
  color:#fff; border-color:var(--mj-red) }
.mj-pstrip__also{ margin:var(--sp-4) 0 0; padding-top:var(--sp-3);
  border-top:1px solid var(--mj-rule); font-size:.88rem; line-height:1.5;
  color:var(--mj-text-muted) }
body.dark-mode .mj-pstrip__also{ border-top-color:#2a2a2d; color:#a8a8ae }
.mj-pstrip__stamp{ margin:6px 0 0; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:.3px; line-height:1.5; color:var(--mj-gray-400) }
@media(max-width:560px){
  .mj-pstrip__cards{ grid-template-columns:minmax(0,1fr) }
  .mj-pstrip__card{ padding-left:var(--sp-3) }
}

/* ── THE SOFTWARE BOX'S MARK AND ITS SAVING ───────────────────────────────
   A software product has no photograph to show, so the company's mark is the
   only thing that makes the card recognisable at a glance — and the box was
   shipping without one, which is most of why a well-written software box still
   read as scaffolding beside a hardware box that has a product shot.

   Capped at 20px on its own line above the eyebrow: it names the party, it does
   not advertise them. */
/* SIZED ON WIDTH, NOT HEIGHT. A vendor mark is usually a horizontal lockup —
   iKhokha's ships at 463×54 — so capping the HEIGHT at 20px and the width at
   120px let the width win and rendered the wordmark 14px tall and illegible.
   Marks are recognised by their shape at reading size, so the width is what is
   given room and the height is only a ceiling for the rare square mark. */
.mj-sb__mark{ display:block; margin:0 0 var(--sp-3) }
.mj-sb__mark img{ width:auto; max-width:150px; max-height:26px; object-fit:contain;
  object-position:left center }
body.dark-mode .mj-sb__mark img{ filter:brightness(0) invert(1); opacity:.9 }

/* The price line becomes a ladder when there is a saving: what it lists at,
   what you pay, the cadence, and the saving as a chip. Same green, same shape
   and same rule as the hardware ladder — one idea of what a discount looks
   like across the site, rather than one per element. */
.mj-sb__was{ font-size:.9rem; color:var(--mj-gray-400);
  text-decoration:line-through; text-decoration-thickness:1px; margin-right:6px }
.mj-sb__off{ display:inline-flex; align-items:center; gap:4px;
  font-family:var(--font-meta); font-size:.6rem; font-weight:700;
  letter-spacing:.8px; text-transform:uppercase; color:#0b5c33;
  background:var(--mj-green-100, #d8f3e3); border-radius:999px;
  padding:3px 8px; margin-left:8px; white-space:nowrap }
body.dark-mode .mj-sb__off{ color:#8ce0b0; background:rgba(16,120,70,.22) }

/* ── THE SECONDARY TRAY ───────────────────────────────────────────────────
   Reveal earns its keep here rather than on the main panel. The printing
   models are a real option and a minority one, so they are offered by name in
   a sentence and shown on request — which is the exact shape reveal was built
   for and the exact shape the short answer was misusing it as.
   Divided from the sentence above by a rule, so an opened tray reads as an
   extension of the card and not as a new element that has appeared. */
.mj-pstrip__tray[hidden]{ display:none }
.mj-pstrip__tray{ margin-top:var(--sp-4); padding-top:var(--sp-4);
  border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-pstrip__tray{ border-top-color:#2a2a2d }

/* ── FEE TABLE + CTA · the ONE table allowed to convert ───────────────────
   THE LOAD-BEARING DISTINCTION IN THIS WHOLE REQUEST. A table in the BODY of an
   article is information only — no CTAs, no buttons, nothing that sells. This
   variant is permitted to convert and permitted ONLY inside a short-answer
   reveal, where the reader opened it deliberately. If that boundary blurs, the
   body of every article becomes a shop again, which is what this pass is
   undoing. It is a separate element from `comparison_table` for exactly that
   reason: two names, so the boundary is visible in the source. */
.mj-fct{ border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  background:var(--mj-surface); overflow:hidden }
body.dark-mode .mj-fct{ background:#141416; border-color:#2a2a2d }
.mj-fct__lead{ margin:0; padding:var(--sp-4) var(--sp-5) var(--sp-3);
  font-size:.9rem; line-height:1.55; color:var(--mj-text-muted) }
body.dark-mode .mj-fct__lead{ color:#a8a8ae }
/* named scroll, and it genuinely scrolls — the layout audit checks that */
.mj-fct__scroll{ overflow-x:auto; -webkit-overflow-scrolling:touch }
.mj-fct__t{ width:100%; border-collapse:collapse; min-width:min(460px,100%) }
.mj-fct__t th{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--mj-gray-400); text-align:left; font-weight:600;
  padding:8px var(--sp-5); border-bottom:1px solid var(--mj-rule); white-space:nowrap }
.mj-fct__t td{ padding:10px var(--sp-5); border-bottom:1px solid var(--mj-rule);
  font-size:.88rem; line-height:1.4; vertical-align:top }
body.dark-mode .mj-fct__t th,body.dark-mode .mj-fct__t td{ border-bottom-color:#2a2a2d }
.mj-fct__t tbody tr:last-child td{ border-bottom:0 }
/* THE ROW LABEL WRAPS. `nowrap` was written for a provider name — "Yoco",
   "WAPPoint" — and a one-word name never breaks either way. The invoicing
   panels put SENTENCES in this column ("Your accountant asks for access",
   "The customer's name, address and VAT number"), and unbreakable they pushed
   the table to 716px and 732px inside a 678px card at every desktop width, so
   the last column was cut off at the card's edge. Measured, not eyeballed. */
.mj-fct__who{ font-family:var(--font-display); font-weight:700; white-space:normal;
  color:var(--mj-black) }
body.dark-mode .mj-fct__who{ color:#fff }
/* the cheaper one is marked, because a table that makes the reader do the
   comparison it was built to do has not finished its job */
.mj-fct__row.is-best{ background:var(--rb-green-soft) }

/* ── THE TABLE THAT CONVERTS ──────────────────────────────────────────────
   Lucky, on the tables in the short answer: "they feel more informative than
   to make someone make a purchase." A table that answers WHICH OF THESE and
   then stops hands the reader a decision with nowhere to take it.

   Three additions, and the restraint matters as much as they do: a table
   explaining what SARS requires has nothing to sell, so this is a MODIFIER the
   payload asks for (`convert: true`) and never something inferred. */

/* 1 · THE ROW YOU CAN ACT FROM. Beside the price it belongs to, so a scanner
       never has to go and find the buying moment somewhere else. Right-aligned
       and narrow: it is the end of the row, not a fourth column of data. */
.mj-fct__acth, .mj-fct__act{ width:1%; white-space:nowrap; text-align:right }
/* THE HEADER OF THAT COLUMN IS SHAPED BY POSITION, NOT BY A CLASS.
   The renderer repeats ONE header cell per column, so a class carried by only
   the last one in the prototype cannot survive the repeat — the action column
   would render as an ordinary header and take an equal share of the width,
   squeezing the figures the table exists to show. Position is the one thing
   the repeat cannot lose. */
.mj-fct--convert .mj-fct__t thead th:last-child{
  width:1%; white-space:nowrap; text-align:right }
.mj-fct__rowcta{ display:inline-block; font-family:var(--font-meta); font-size:.62rem;
  font-weight:700; letter-spacing:.8px; text-transform:uppercase;
  color:var(--mj-black); background:var(--mj-surface); text-decoration:none;
  border:1.5px solid var(--mj-black); border-radius:var(--radius-sm);
  padding:7px 12px; transition:background .14s ease, color .14s ease }
.mj-fct__rowcta:hover{ background:var(--mj-black); color:#fff }
body.dark-mode .mj-fct__rowcta{ color:#fff; border-color:#fff; background:transparent }
body.dark-mode .mj-fct__rowcta:hover{ background:#fff; color:#0c0c0e }
/* The best row's button is the one that should be pressed, so it is filled. */
.mj-fct__row.is-best .mj-fct__rowcta{ background:var(--mj-red); border-color:var(--mj-red);
  color:#fff }
.mj-fct__row.is-best .mj-fct__rowcta:hover{ background:#c4161b; border-color:#c4161b }

/* ── THE TABLE TAKES PANEL C'S DIRECTION (Lucky, 2026-09-16) ──────────────
   "Make sure that the tables are a little bit more interesting and take the
   direction of the panel C designs. On these tables where there are CTA
   sections, make sure that the buttons adopt the colours of the brands that
   have been showcased."

   Panel C is the pick rail: every card carries the vendor's own mark, the name
   in the display face, and a button in the vendor's colour. A table of the same
   four providers was doing none of that — it was a price list with a black
   button on every row, so nothing in it said WHO, only WHAT. The mark and the
   accent are what make a row feel like that brand's row.

   THE ACCENT IS OPTIONAL AND THAT IS THE POINT. A row with no
   `--brand-accent` — a SARS requirement, a bank that publishes nothing — keeps
   the black outline exactly as it was. Nothing is inferred and nothing is
   invented: a colour appears only where a brand was actually named. */
.mj-fct__who{ display:flex; align-items:center; gap:10px }
/* The name is its own element so the mark beside it can be dropped by the
   template where a row names no brand — a SARS requirement has no logo, and
   an empty frame is worse than none. */
.mj-fct__wholabel{ min-width:0 }
.mj-fct__mark{ flex:0 0 auto; display:flex; align-items:center; justify-content:center;
  width:78px; height:32px; padding:0 7px; background:var(--mj-surface-2);
  border-radius:var(--radius-sm) }
body.dark-mode .mj-fct__mark{ background:#1b1b1e }
.mj-fct__mark img{ max-width:64px; max-height:22px; width:auto; height:auto;
  object-fit:contain }
/* The best row already wears the green of a win; the accent names WHOSE win it
   is, on the edge, where it cannot argue with the tint. */
.mj-fct__row.is-best td:first-child{
  box-shadow:inset 3px 0 0 var(--brand-accent, var(--mj-growth)) }

/* THE BUTTON IN THE VENDOR'S COLOUR — outlined on an ordinary row, filled on
   the row the table itself recommends, which is the same grammar the product
   boxes and the pick rail use. */
.mj-fct__row[style*="--brand-accent"] .mj-fct__rowcta{
  color:var(--mj-black);
  border-color:color-mix(in srgb, var(--brand-accent) 62%, #ffffff) }
.mj-fct__row[style*="--brand-accent"] .mj-fct__rowcta:hover{
  background:var(--brand-accent); border-color:var(--brand-accent);
  color:var(--brand-ink, #fff) }
.mj-fct__row.is-best[style*="--brand-accent"] .mj-fct__rowcta{
  background:var(--brand-accent); border-color:var(--brand-accent);
  color:var(--brand-ink, #fff) }
.mj-fct__row.is-best[style*="--brand-accent"] .mj-fct__rowcta:hover{
  background:color-mix(in srgb, var(--brand-accent) 82%, #000);
  border-color:color-mix(in srgb, var(--brand-accent) 82%, #000) }
body.dark-mode .mj-fct__row[style*="--brand-accent"] .mj-fct__rowcta{
  color:#fff; border-color:color-mix(in srgb, var(--brand-accent) 52%, #0c0c0e) }

/* 2 · THE SAVING, IN THE PRICE CELL, in the one chip this site uses for that
       fact. On its own line under the figure so the number stays the thing the
       eye lands on. */
/* AIR BETWEEN THE FIGURE AND WHAT COMES OFF IT. The chip sat flush against the
   price, so "R599+ R100 OFF" read as one token. They are two facts and the eye
   needs the join to be visible. */
.mj-fct__c .mj-pb__pay-tag{ display:inline-flex; margin-left:8px;
  vertical-align:middle }
@media(max-width:700px){
  .mj-fct__c .mj-pb__pay-tag{ display:inline-flex; margin-left:0; margin-top:6px }
}

/* 3 · THE INTERACTIVE FOOTER, for the reader who can see four prices and still
       does not know which is theirs. It is the finder, embedded — same
       machinery, same weights, no new JavaScript — and `inserts.js` keeps its
       result hidden until an option is pressed, because a recommendation
       before a question is just an advert. */
/* NO FRAME IN A FRAME. The deciding footer was a grey band, and the element it
   hosts now brings its own white ground and red bar — a card inside a band is
   the one shape this design system does not use. The band keeps only the job of
   holding the lead-in; the bar below it does the separating. */
.mj-fct__decide{ border-top:1px solid var(--mj-rule);
  padding:var(--sp-4) var(--sp-5) 0; background:#fff }
body.dark-mode .mj-fct__decide{ border-top-color:#2a2a2d; background:#141416 }
.mj-fct__decide .mj-finder[data-layout="embed"]{
  margin-inline:calc(var(--sp-5) * -1); margin-bottom:0 }
.mj-fct__decide-lead{ margin:0 0 var(--sp-4); font-size:.86rem; line-height:1.55;
  color:var(--mj-text-muted) }
/* NO BACKGROUND OVERRIDE HERE, deliberately.
   The first version painted this panel white to match the table above it, and
   the answered state's TEXT is inverted for the red ground it is designed to
   sit on — so the recommendation rendered white on white and was invisible.
   The band going red the moment it is answering THIS reader is the design
   working: it is the one thing on the page that is now about them. */

/* THE RECOMMENDATION APPEARS WHEN IT HAS SOMETHING TO RECOMMEND.
   The standing finder shows its result before any question is answered, and
   that is right THERE — it is labelled "Our standing pick" and it is the
   desk's position, which a reader is entitled to see without answering
   anything. In a table footer it is wrong: the table has just shown three
   prices, and a "Your match" with a live buy button before a single question
   has been pressed is not a recommendation, it is an advert wearing one's
   clothes. Lucky asked for a "conditional CTA element that will show if they
   start using the interactive part", and this is that condition.
   `inserts.js` already stamps `data-state="answered"`; no new script. */
.mj-fct__decide .mj-finder__result:not([data-state="answered"]){ display:none }

/* Inside a short-answer panel the table has already lost its frame, so the
   footer should not draw one of its own either — it is divided by a rule and
   nothing more. */
/* THE DECIDING FOOTER IS PART OF THE CARD, NOT A THING SITTING ON IT.
   It was inset by the card's padding on both sides and floated 49px clear of
   the bottom, so the interactive section and the recommendation it produces
   read as a separate object that had landed inside the element. Lucky: "make
   sure the final CTA element doesn't leave gaps to the left and the right and
   the bottom, so that what appears as a CTA feels unified to the element."
   Pulled to all three edges, and the card's own radius finishes it. */
.mj-vstrip__panel .mj-fct__decide{
  margin:0;
  padding:var(--sp-5) var(--pb-pad, var(--sp-5)) 0;
  background:#fff;
  border-radius:0 0 var(--radius-md) var(--radius-md) }
body.dark-mode .mj-vstrip__panel .mj-fct__decide{ background:#141416 }
/* The hosted finder reaches the card's edges, so its bar is the card's own
   divider rather than a line floating inside a padded band. */
.mj-vstrip__panel .mj-fct__decide .mj-finder[data-layout="embed"]{
  margin-inline:calc(var(--pb-pad, var(--sp-5)) * -1);
  padding-inline:var(--pb-pad, var(--sp-5)) }
/* And the answered band fills that footer rather than sitting inside it with a
   margin of its own — one object, not two. */
/* The finder clips itself for a radius it does not have in here, and that
   clip is what was beheading the answered band's first letter. */
.mj-vstrip__panel .mj-fct__decide .mj-finder{ overflow:visible }
.mj-vstrip__panel .mj-fct__decide .mj-finder__result{
  margin:var(--sp-4) calc(var(--pb-pad, var(--sp-5)) * -1)
         calc(var(--sp-5) * -1);
  padding:var(--sp-5) var(--pb-pad, var(--sp-5));
  border-radius:0 0 var(--radius-md) var(--radius-md) }
/* THE TABLE LOSES ITS FRAME, NOT ITS INSET.
   Zeroing the edge padding was an over-correction: the first column's text then
   sat hard against the card's inner edge, and the highlighted row's green ran
   to the boundary with nothing between the tint and the words. Lucky: "the text
   in the rows is far to the right, far to the left, and when there is a green
   highlight there's no padding to the right of those elements."
   A row's tint needs to breathe on both sides or it reads as a bleed rather
   than as a highlight. The frame is what was removed; the inset stays. */
/* THE TINT REACHES THE CARD'S EDGE; THE TEXT DOES NOT.
   These two were fighting: the scroll wrapper was pulled out by one step and
   the cells were padded back in by the same step, so the net inset was ZERO
   and the highlighted row's first word sat exactly on the green. Measured:
   `leftInset: 0`.
   The tint is pulled out by the CARD'S OWN PADDING so the band spans the full
   width — a highlight that stops short of the edge reads as a floating stripe —
   and the cells are padded by that same amount PLUS a step, so the words are
   inset from the colour behind them on both sides. */
/* ONE LEFT EDGE FOR THE WHOLE CARD.
   The container already reaches the card's edges, so nothing in here needs a
   negative margin of its own — each element simply pads back in by the card's
   padding, and every line in the element starts on the same vertical: the
   table's first column, the caption, the deciding lead, the question, and the
   answered band's kicker. The row tint still runs edge to edge behind them. */
.mj-vstrip__panel .mj-fct__scroll{ margin:0 }
.mj-vstrip__panel .mj-fct__t th:first-child,
.mj-vstrip__panel .mj-fct__t td:first-child{
  padding-left:var(--pb-pad, var(--sp-5)) }
.mj-vstrip__panel .mj-fct__t th:last-child,
.mj-vstrip__panel .mj-fct__t td:last-child{
  padding-right:var(--pb-pad, var(--sp-5)) }
.mj-vstrip__panel .mj-fct__cap,
.mj-vstrip__panel .mj-fct__lead{
  padding-left:var(--pb-pad, var(--sp-5));
  padding-right:var(--pb-pad, var(--sp-5)) }
body.dark-mode .mj-fct__row.is-best{ background:rgba(13,185,128,.08) }
.mj-fct__fig{ font-weight:700; color:var(--mj-black) }
body.dark-mode .mj-fct__fig{ color:#fff }
.mj-fct__note{ display:block; font-size:.75rem; color:var(--mj-gray-400); margin-top:2px }
.mj-fct__cap{ margin:0; padding:var(--sp-3) var(--sp-5);
  font-family:var(--font-meta); font-size:.62rem; letter-spacing:.3px;
  color:var(--mj-gray-400) }
.mj-fct__foot{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--sp-4);
  padding:var(--sp-4) var(--sp-5); border-top:1px solid var(--mj-rule);
  background:#fff }
body.dark-mode .mj-fct__foot{ border-top-color:#2a2a2d; background:#141416 }
.mj-fct__foot-line{ flex:1 1 220px; margin:0; font-size:.88rem; line-height:1.5;
  color:var(--mj-charcoal) }
body.dark-mode .mj-fct__foot-line{ color:#d9d9dd }
.mj-fct__cta{ display:inline-flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; text-transform:uppercase;
  font-size:.74rem; letter-spacing:.3px; padding:11px 18px; white-space:nowrap;
  background:var(--link-action); color:#fff; text-decoration:none;
  border-radius:var(--radius-sm); transition:background .18s ease }
.mj-fct__cta:hover{ background:var(--mj-red-hover) }
@media(max-width:560px){
  .mj-fct__lead,.mj-fct__cap,.mj-fct__foot{ padding-left:var(--sp-4); padding-right:var(--sp-4) }
  .mj-fct__t th,.mj-fct__t td{ padding-left:var(--sp-4); padding-right:var(--sp-4) }
  .mj-fct__cta{ width:100% }
}

/* ── SOFTWARE MONETISATION (2026-09-05) ──────────────────────────────────────
   The product box was built for a card machine: photo, once-off rand price,
   physical specs. Against a SaaS product it drew an empty photo column, an
   aria-label reading "card machine" for accounting software, and no price at
   all — the parser only understood strings starting with "R".

   A subscription has a different shape and these admit it: the price is a
   cadence or a STATE (free forever / not published), and the decisive datum is
   the LIMIT rather than the spec. Deliberately named `mj-sb` and `mj-tcta`
   rather than anything invoicing-specific, because hosting, CRM, email and
   course platforms are all this shape. */
/* ── THE REVIEW BLOCK'S BORDER SYSTEM, IN THE VENDOR'S OWN COLOUR ─────────
   Lucky: "adopt the same border system of the review blocks where you have a
   colour border at the top, and where the CTA section starts, and at the
   bottom — and the colour must always be the vendor's own colour."

   Three accented edges, and each one is doing a different job:
     TOP     says whose product this is before a word is read
     CTA     says the page has changed register — you have stopped reading
             about it and started being asked to buy it
     BOTTOM  closes the object, so a run of boxes reads as separate cards
             rather than as one long column

   `--sb-accent` is DATA, not a styling decision: it carries the vendor's own
   `brand_primary_color` in from the entity store, which is how the brand hubs
   already work (`--brand-accent` on `.mj-ent-hero`). `validators.check_l0`
   allows a custom property in markup for exactly this reason and bans a real
   declaration. A brand with no recorded colour falls back to the house red and
   the build says which brand, because a silent fallback is how every box ends
   up the same colour and nobody notices. */
.mj-sb{ --sb-pad:var(--sp-5); --sb-accent:var(--mj-red); overflow:hidden;
  border:1px solid var(--mj-rule);
  border-top:4px solid var(--sb-accent);
  border-bottom:3px solid var(--sb-accent);
  border-radius:var(--radius-md);
  padding:var(--sb-pad); margin:var(--sp-6) 0; background:var(--mj-surface) }
body.dark-mode .mj-sb{ background:#141416;
  border-left-color:#2a2a2d; border-right-color:#2a2a2d }
.mj-sb__head{ display:flex; align-items:baseline; gap:var(--sp-3); flex-wrap:wrap }
.mj-sb__eyebrow{ margin:0; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-red) }
.mj-sb__name{ margin:2px 0 0; font-family:var(--font-display); font-weight:800;
  font-size:1.35rem; line-height:1.15; flex:1 1 100% }
body.dark-mode .mj-sb__name{ color:#fff }
.mj-sb__badge{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:.8px;
  text-transform:uppercase; color:var(--mj-trust); white-space:nowrap }

/* THE PRICE IS A CADENCE OR A STATE. Both render; neither is optional-looking. */
.mj-sb__price{ display:flex; align-items:baseline; gap:10px; flex-wrap:wrap;
  margin:var(--sp-4) 0 var(--sp-3) }
.mj-sb__amount{ font-family:var(--font-display); font-weight:800; font-size:2rem;
  line-height:1; color:var(--mj-black) }
body.dark-mode .mj-sb__amount{ color:#fff }
.mj-sb__cadence{ font-family:var(--font-meta); font-size:.72rem; letter-spacing:.6px;
  text-transform:uppercase; color:var(--mj-gray-400) }
.mj-sb__state{ font-family:var(--font-meta); font-size:.64rem; letter-spacing:.8px;
  text-transform:uppercase; padding:4px 9px; border-radius:999px; white-space:nowrap }
.mj-sb__state--free{ background:var(--mj-growth); color:#fff }
/* An unverifiable price is stated, not hidden — a blank price slot reads as an
   oversight, and this is a finding. */
.mj-sb__state--unpriced{ background:transparent; color:var(--mj-text-muted);
  border:1px dashed var(--mj-rule); text-transform:none; letter-spacing:0;
  font-family:var(--font-serif); font-size:.9rem; padding:6px 10px }

/* THE CAP IS THE HERO DATUM on a free tier, so it gets its own line above the
   specs rather than being the fourth row of a list nobody reads. */
.mj-sb__cap{ margin:0 0 var(--sp-4); padding:10px 12px; border-radius:var(--radius-sm);
  background:var(--mj-surface-2); display:flex; gap:10px; flex-wrap:wrap; align-items:baseline }
body.dark-mode .mj-sb__cap{ background:#1b1b1f }
.mj-sb__cap-k{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--mj-gray-400) }
.mj-sb__cap-v{ font-family:var(--font-display); font-weight:700; font-size:.95rem }
.mj-sb__specs{ list-style:none; margin:0 0 var(--sp-4); padding:0 }
.mj-sb__specs li{ display:flex; gap:12px; padding:7px 0; border-top:1px solid var(--mj-rule);
  font-size:.9rem }
.mj-sb__specs .k{ flex:0 0 40%; color:var(--mj-text-muted) }
.mj-sb__specs .v{ flex:1 1 auto; font-weight:600 }
.mj-sb__actions{ display:flex; gap:10px; flex-wrap:wrap; align-items:center }
.mj-sb__cta{ display:inline-flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; text-transform:uppercase;
  font-size:.72rem; letter-spacing:.3px; padding:10px 16px; text-decoration:none;
  border-radius:var(--radius-sm); background:var(--mj-red); color:#fff;
  border:1px solid var(--mj-red); transition:.16s }
.mj-sb__cta:hover{ background:var(--mj-red-hover); border-color:var(--mj-red-hover) }
.mj-sb__cta--ghost{ background:transparent; color:var(--mj-black);
  border-color:var(--mj-ink-900) }
body.dark-mode .mj-sb__cta--ghost{ color:#fff; border-color:#5a5a60 }
.mj-sb__disclosure{ margin:10px 0 0; font-size:.78rem; color:var(--mj-text-muted) }

/* ── EVERY BOX'S PRIMARY BUTTON IS THE PRIMARY BUTTON ─────────────────────
   `.mj-sb--free` and `.mj-sb--unpriced` carried a rule quietly turning their
   CTA dark, on the reasoning that "a free tool earns nothing, so its box does
   not shout". That is a judgement about OUR revenue expressed as a design
   difference the reader has to interpret — and a reader cannot know why one
   button is black and the next is red, so all they learn is that the buttons
   are inconsistent. What the free tier earns us is our business; which button
   is the action is theirs. The disclosure already says the honest thing in
   words. */
.mj-sb--free .mj-sb__cta:not(.mj-sb__cta--ghost),
.mj-sb--unpriced .mj-sb__cta:not(.mj-sb__cta--ghost){
  background:var(--mj-red); border-color:var(--mj-red); color:#fff }
.mj-sb--free .mj-sb__cta:not(.mj-sb__cta--ghost):hover,
.mj-sb--unpriced .mj-sb__cta:not(.mj-sb__cta--ghost):hover{
  background:var(--mj-red-hover); border-color:var(--mj-red-hover) }

/* ── THE RANK, WHERE A LIST NEEDS ONE ─────────────────────────────────────
   On a list or comparison article the position IS information, and it belongs
   beside the reason rather than above the name where it would compete with it.
   Absent everywhere else: a single review has no rank and inventing one would
   imply a field that was never run. */
.mj-sb__eyebrow-t{ display:inline }
.mj-sb__rank{ display:inline-flex; align-items:center; justify-content:center;
  min-width:22px; height:22px; padding:0 6px; margin-right:8px;
  font-family:var(--font-display); font-weight:800; font-size:.72rem;
  color:#fff; background:var(--sb-accent); border-radius:var(--radius-sm) }

/* ── THE CTA REGION, ON THE REVIEW BLOCK'S STANDARD ───────────────────────
   A lone red button is not a call to action, it is a link with weight. The
   review block's Variation 3 answers four questions at the point of pressing —
   what the saving is, what moment this is, what else I could do, and where the
   price came from — and this element answered none of them.
   Same four parts, same order, so a reader who has met one recognises the
   other. That recognition is the whole reason to have a standard. */
/* ── THE CLOSE, AS A BAND ─────────────────────────────────────────────────
   `.rb-f-close` on the review block is a dark band, and the reason it works is
   that the commercial moment reads as a DESTINATION rather than as the end of
   the text. On a light card the same four parts simply trailed off. Same
   colour, same ghost treatment, same source line — a reader who has met one
   recognises the other, which is the whole reason to have a standard.
   Full-bleed to the card's edges, so the band IS the bottom of the card. */
.mj-sb__close{ margin:var(--sp-5) calc(var(--sb-pad, var(--sp-5)) * -1)
    calc(var(--sb-pad, var(--sp-5)) * -1);
  padding:var(--sp-5) var(--sb-pad, var(--sp-5));
  background:#121214; color:#f2f2f2;
  /* THE THIRD EDGE. The band already changes the ground; the accent rule on top
     of it is what makes the change deliberate rather than incidental. */
  border-top:3px solid var(--sb-accent);
  border-radius:0 }
.mj-sb__close .mj-sb__paynote{ color:#c9c9c9 }
.mj-sb__close .mj-sb__ctahead{ color:#fff }
.mj-sb__close .mj-sb__checked{ color:#9a9a9a }
.mj-sb__close .mj-sb__src{ color:#c9c9c9 }
.mj-sb__close .mj-sb__disclosure{ color:#8f8f95 }
.mj-sb__close .mj-sb__cta--ghost{ color:#f2f2f2; border-color:#4a4a4f;
  background:transparent }
.mj-sb__close .mj-sb__cta--ghost:hover{ background:#26262b; color:#fff }

.mj-sb__paynote{ margin:0 0 var(--sp-3); font-size:.82rem; line-height:1.5;
  color:var(--mj-text-muted) }
.mj-sb__ctahead{ margin:0 0 var(--sp-3); font-family:var(--font-display);
  font-weight:800; font-size:1.02rem; color:var(--mj-black) }
body.dark-mode .mj-sb__ctahead{ color:#fff }
/* The source sits ON the row rather than under it: it is a qualifier on the
   button beside it, and a reader deciding whether to press wants it there. */
.mj-sb__checked{ font-family:var(--font-meta); font-size:.66rem; letter-spacing:.2px;
  color:var(--mj-gray-400) }
.mj-sb__src{ color:inherit; text-underline-offset:2px }

/* ── THE PRICE, COLOUR-CODED THE WAY THIS DESK CODES PRICES ───────────────
   Lucky: "apply the right design skills, or the way we are doing it in
   SmartCMS, so that prices can stand out." The three states MEAN different
   things and were rendered in one weight of black:
     priced    a number you will pay        — the assertive face, full ink
     free      a number you will not pay    — the growth colour, because R0 is
                                              a saving and this desk says so in
                                              green everywhere else
     unpriced  a finding, not a number      — muted, because the honest answer
                                              to "what does it cost" is a
                                              sentence and must not wear the
                                              costume of a figure */
.mj-sb--free .mj-sb__amount{ color:var(--mj-growth-text) }
body.dark-mode .mj-sb--free .mj-sb__amount{ color:#7fd8ae }
.mj-sb--free .mj-sb__cadence{ color:var(--mj-growth-text); opacity:.85 }
body.dark-mode .mj-sb--free .mj-sb__cadence{ color:#7fd8ae }
.mj-sb__state--unpriced{ font-family:var(--font-body); font-size:.94rem;
  line-height:1.45; color:var(--mj-text-muted); font-style:italic }
/* The priced variant is the base look; the modifier exists so the render can
   name which of the three price states a box is in, and L0 requires every class
   we emit to have a rule.

   `border-color` HERE WOULD HAVE ERASED THE ACCENT. The shorthand sets all four
   edges, so `.mj-sb--priced{border-color:var(--mj-rule)}` — written before the
   accent existed — would have painted the top and bottom grey again on every
   priced box, silently, and only on that variant. The side edges are named
   explicitly now.

   AND THE BUTTON RULES ARE GONE. `--free` turned its CTA dark and `--unpriced`
   made it a ghost, on the reasoning that a free tool earns nothing so its box
   should not shout. That is a judgement about OUR revenue expressed as a design
   difference the reader has to interpret — and they cannot know why one button
   is black and the next is red, so all they learn is that the buttons are
   inconsistent. What a tier earns us is our business; which button is the
   action is theirs, and the disclosure already says the honest thing in words. */
.mj-sb--priced{ border-left-color:var(--mj-rule); border-right-color:var(--mj-rule) }

/* ── TRIAL CTA: what the upgrade buys ──────────────────────────────────────
   A software CTA cannot say "buy". The decision is free-versus-paid, so the
   element shows the two columns side by side and prices the difference. */
/* ── THE TRIAL CTA, ON THE REVIEW BLOCK'S CHASSIS ─────────────────────────
   Lucky: "the element feels too square, so adopt the rounding of the outside
   elements as in the review blocks."

   The outer radius is not what he is seeing — this desk is square BY DIRECTIVE
   and `--radius-md` is 2px everywhere. What makes the review block feel less
   hard is two other things: a 4px accent stripe along the top instead of a
   2px box drawn all the way round, and 6px on the INNER cells and buttons. A
   heavy full border is what reads as a box; a top stripe reads as a card.
   Same chassis here, so the two elements sit together on a page. */
.mj-tcta{ border:1px solid var(--mj-rule); border-top:4px solid var(--mj-red);
  border-radius:var(--radius-md); overflow:hidden;
  padding:var(--sp-5); margin:var(--sp-6) 0; background:var(--mj-surface) }
body.dark-mode .mj-tcta{ border-color:#2a2a2d; border-top-color:var(--mj-red);
  background:#161618 }
.mj-tcta__k{ margin:0; font-family:var(--font-meta); font-size:.62rem;
  letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-red) }
.mj-tcta__h{ margin:4px 0 var(--sp-4); font-family:var(--font-display);
  font-weight:800; font-size:1.25rem; line-height:1.2 }
body.dark-mode .mj-tcta__h{ color:#fff }
.mj-tcta__cols{ display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-4) }
.mj-tcta__col{ padding:var(--sp-4); border-radius:6px;
  background:var(--mj-surface-2) }
body.dark-mode .mj-tcta__col{ background:#1b1b1f }
/* The disambiguating modifiers exist so extraction can tell the two columns
   apart; L0 requires a rule for every class the render emits. */
.mj-tcta__col--free{ }
.mj-tcta__list--free, .mj-tcta__list--paid{ }
.mj-tcta__col--paid{ background:transparent; border:2px solid var(--mj-red) }
.mj-tcta__col-h{ margin:0 0 8px; font-family:var(--font-meta); font-size:.6rem;
  letter-spacing:1.2px; text-transform:uppercase; color:var(--mj-gray-400) }
.mj-tcta__list{ margin:0; padding-left:18px; font-size:.9rem; line-height:1.5 }
.mj-tcta__list li{ margin:0 0 4px }
.mj-tcta__line{ margin:var(--sp-4) 0 var(--sp-3); font-family:var(--font-display);
  font-weight:700; font-size:1rem }
.mj-tcta__cta{ display:inline-flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; text-transform:uppercase;
  font-size:.72rem; letter-spacing:.3px; padding:11px 18px; text-decoration:none;
  border-radius:6px; background:var(--mj-red); color:#fff }
.mj-tcta__cta:hover{ background:var(--mj-red-hover) }
.mj-tcta__note{ margin:10px 0 0; font-size:.76rem; color:var(--mj-text-muted) }
@media(max-width:560px){
  .mj-tcta__cols{ grid-template-columns:1fr }
  .mj-sb__specs li{ flex-direction:column; gap:2px }
  .mj-sb__specs .k{ flex:0 0 auto }
}

/* ── THE RANKED CARD WEARS THE VENDOR'S COLOUR (2026-09-12) ──────────────────
   Lucky: the product boxes should carry company colour borders and a CTA
   section. `--brand-accent` comes from the brand's own logo, recorded in
   site.json `brand_colors`; `--brand-ink` is the readable text on it, decided
   in the renderer because CSS cannot measure contrast (GoSolr's yellow needs
   near-black, Alumo's green needs white). Both fall back, so a brand with no
   recorded colour renders exactly as it did before. */
/* THE VENDOR'S RULE RUNS ACROSS, NOT DOWN (Lucky, 2026-09-16).
   "Instead of having the border to the left, you should have the border at the
   top and also at the bottom, and the border should still be representing the
   product that is being shown."
   A left rule is this site's SHORT-ANSWER mark — the red bar down the verdict
   strip — so a second left rule on the biggest product element was the same
   gesture meaning two different things. Top and bottom brackets the card: it
   reads as a bounded object that belongs to one vendor, and it cannot be
   confused with the answer bar. */
.mj-pb--rank{ --brand-accent: var(--mj-ink-900); --brand-ink:#fff;
  border-left:0; border-right:0;
  border-top:4px solid var(--brand-accent);
  border-bottom:4px solid var(--brand-accent) }
.mj-pb--rank .rank-num i{ background:var(--brand-accent); color:var(--brand-ink) }
body.dark-mode .mj-pb--rank .rank-num i{ background:var(--brand-accent); color:var(--brand-ink) }
.mj-pb--rank .mj-pb__cta:not(.mj-pb__cta--ghost){ background:var(--brand-accent);
  border-color:var(--brand-accent); color:var(--brand-ink) }
.mj-pb--rank .mj-pb__fit{ margin:0 0 14px }
.mj-pb--rank .mj-pb__fit .row{ display:flex; gap:12px; align-items:baseline; font-size:.92rem;
  padding:9px 0; border-top:1px solid var(--mj-rule) }
.mj-pb--rank .mj-pb__fit .row b{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-text-muted); min-width:78px; flex:none }
.mj-pb--rank.is-pick{ border-top-color:var(--brand-accent);
  border-bottom-color:var(--brand-accent) }

/* ── THE NAV CLASSES THE LIBRARY NEVER KNEW (2026-09-12) ────────────────────
   `validators.check_l0` has warned on every build that nine emitted classes have
   no library rule: mm-item, mm-top, mm-flat, m-kicker, md-acc__t, thumb-chip,
   nav-global, chrome-mega, foot-legal. The library law is style them or stop
   emitting them, and unstyled markup is how the first HTML site died. */
.mm-item{ position:static }
.mm-item>.mm-top,.mm-flat>.mm-top{ padding-block:var(--sp-2) }

/* "Just in" is a claim about recency, which is true every time we post, so it
   reads as live: a small pulse, and no chevron because it opens no panel.
   Lucky asked for a name with some jolt that is not "breaking news". */
/* THE BOLT (Lucky, 2026-09-20): "change the icon for the Just In section so that
   it looks like the one on the Newsreader theme … which looks more like a
   lightning sign … but keep it blinking a little". A masked bolt takes the red
   the dot had, so it still reads as live and nothing else about the item moves. */
.mm-flat>.mm-top::before{ content:""; display:inline-block; width:11px; height:11px;
  margin-right:6px; background:var(--mj-red); vertical-align:-1px;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M13.6 1.5 4.2 13.3c-.4.5 0 1.2.6 1.2h4.5l-1.2 7.6c-.1.7.8 1.1 1.2.5l9.5-11.8c.4-.5 0-1.2-.6-1.2h-4.5l1.2-7.6c.1-.7-.8-1.1-1.3-.5Z'/%3E%3C/svg%3E") center/contain no-repeat; mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M13.6 1.5 4.2 13.3c-.4.5 0 1.2.6 1.2h4.5l-1.2 7.6c-.1.7.8 1.1 1.2.5l9.5-11.8c.4-.5 0-1.2-.6-1.2h-4.5l1.2-7.6c.1-.7-.8-1.1-1.3-.5Z'/%3E%3C/svg%3E") center/contain no-repeat;
  animation:mm-live 2.4s ease-in-out infinite }
@keyframes mm-live{ 0%,100%{opacity:1} 50%{opacity:.35} }
@media (prefers-reduced-motion:reduce){ .mm-flat>.mm-top::before{ animation:none } }

.m-kicker{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-text-muted) }
.mm-feat .thumb{ position:relative }
.thumb-chip{ position:absolute; left:10px; top:10px; font-family:var(--font-meta);
  font-size:.58rem; letter-spacing:1px; text-transform:uppercase; padding:4px 8px;
  background:var(--mj-red); color:#fff; border-radius:2px }

/* The drawer's accordion title: below 900px the mega menu becomes the drawer. */
.md-acc__t{ font-family:var(--font-display); font-weight:700; font-size:1rem;
  display:flex; align-items:center; justify-content:space-between; width:100%;
  padding:var(--sp-4) 0; background:none; border:0; border-bottom:1px solid var(--mj-rule);
  color:inherit; text-align:left; cursor:pointer }
.md-acc__t:hover{ color:var(--mj-red) }

/* Body state hooks. They paint nothing themselves; naming them is what keeps the
   L0 gate honest about which classes the library actually knows. */
body.nav-global .mj-contain{ display:none }
body.chrome-mega .mj-nav{ display:flex }
.foot-legal{ font-family:var(--font-meta); font-size:.7rem; color:var(--mj-text-muted) }

/* NO PHOTOGRAPH, NO HOLE (2026-09-12). The rail's feature reserves a 16:10 block
   for a cover shot. None of the desks has one yet, so every panel opened on a
   large empty rectangle with a chip floating in it. Where there is no <img>, the
   thumb collapses to the chip it carries, which is the only information in it. */
.mm-feat .thumb:not(:has(img)){ aspect-ratio:auto; height:auto; background:none;
  border-radius:0; margin-bottom:10px }
.mm-feat .thumb:not(:has(img)) .thumb-chip,
.mm-feat .thumb:not(:has(img)) span{ position:static; display:inline-block }

/* ── THE MEGA MENU, GIVEN THE PAGE AND THE FOCUS (2026-09-12) ───────────────
   Lucky: the panels "make the website feel cheap in how they work". Four things
   were wrong, and none of them was taste alone:

     1. NOTHING DIMMED BEHIND IT. The panel opened over live page content with
        the same brightness, so there was no focus and no sense of a surface.
        The drawer's own scrim sits at z-index 12000, ABOVE the header, so it
        cannot be reused here without covering the menu it is meant to isolate.
        This backdrop sits at 8000: under the header (9000), over the page.
     2. IT WAS A SHALLOW STRIP. A panel 300px tall over a full page reads as an
        afterthought. It now fills the viewport under the bar and scrolls inside
        itself, which is also what gives the columns room to breathe.
     3. THE LAYOUT WAS FIXED AT THREE COLUMNS whatever the group held, so a
        two-column desk left a third of the panel empty. `auto-fit` lets each
        group arrange itself around what it actually has.
     4. TWO BORDERS AND A SHADOW on a panel that now reaches the bottom of the
        screen. A full-height surface needs one hairline at the top; the rest
        was drawing boxes for their own sake. */

/* 1 · the backdrop. No new markup: the menu is the only thing that opens it,
   and pointer-events stay off so the page underneath still takes the click that
   closes the panel. */
body::after{ content:""; position:fixed; inset:0; z-index:8000; pointer-events:none;
  background:rgba(10,10,10,.55); opacity:0; visibility:hidden;
  transition:opacity .2s ease, visibility .2s ease }
body:has(.mj-nav>ul>li.mm-item:hover)::after,
body:has(.mj-nav>ul>li.mm-item.is-open)::after{ opacity:1; visibility:visible }
@media (prefers-reduced-motion:reduce){ body::after{ transition:none } }

/* 2 · the panel fills the page under the bar and scrolls inside itself. */
/* FULL WIDTH, AND AS TALL AS ITS CONTENT NEEDS — not the whole viewport.
   Those two requests fight each other: a panel that covers the entire screen
   leaves nothing behind it to dim, so the focus the backdrop exists to create
   is invisible. Full width with a floor of 58vh gives the columns the real
   estate; the page stays visible under it, dimmed, which is what makes the
   panel read as a surface above the site rather than as part of it. */
/* THE CAP IS WHAT MAKES THE DIMMING MEAN ANYTHING. At content height the panel
   came to 816px in a 900px window and covered the page, so the backdrop had
   nothing to darken. Leaving roughly 200px of dimmed page below it is what
   reads as "a surface over the site" rather than "a new page". */
.mj-nav .mm{ height:auto; min-height:min(34vh,300px); max-height:calc(100dvh - 260px);
  overflow-y:auto; overscroll-behavior:contain;
  border-bottom:1px solid var(--mj-rule); box-shadow:0 32px 64px -32px rgba(0,0,0,.28);
  border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-nav .mm{ box-shadow:0 32px 64px -32px rgba(0,0,0,.7) }
.mj-nav .mm-inner{ max-width:var(--chrome-max); margin:0 auto;
  padding:var(--sp-7) var(--sp-5) var(--sp-8);
  grid-template-columns:minmax(0,1fr) 340px; gap:var(--sp-7);
  align-content:start; align-items:start }

/* 3 · each group arranges itself around what it holds. */
.mj-nav .mm-cols{ grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
  gap:var(--sp-6) var(--sp-7) }

/* 4 · bigger, because there is now room for it to be read rather than scanned. */
.mj-nav .mm-col h4{ font-size:.72rem; letter-spacing:2.2px; padding-bottom:10px;
  margin-bottom:var(--sp-4) }
.mj-nav .mm-col ul{ gap:var(--sp-4) }
.mj-nav .mm-col a{ font-size:1.06rem; line-height:1.35 }
.mj-nav .mm-rail{ border-left:1px solid var(--mj-rule); padding-left:var(--sp-7) }
.mj-nav .mm-rail-h{ font-size:.72rem; letter-spacing:2.2px }
.mj-nav .mm-feat h3{ font-size:1.34rem }

/* On a short screen the panel must not become a scroll trap with nothing above
   the fold: below 820px tall it takes the height it needs, up to the viewport. */
@media (max-height:820px){
  .mj-nav .mm{ height:auto; max-height:calc(100dvh - 72px) }
  .mj-nav .mm-inner{ padding:var(--sp-6) var(--sp-5) }
}

/* ── THE PANEL, SECOND PASS (2026-09-12) ────────────────────────────────────
   Lucky, with a screenshot: remove the dual borders, make the deals section
   interesting, and give editor's pick a real design.

   THE DOUBLE BORDER WAS A DOUBLE ELEMENT. `nav_item_mega` rendered
   `<aside class="mm-rail">` and slotted the rail INTO it, and the rail template
   was extracted from `.mm-rail` as well, so every panel carried two nested
   asides: ten in a five-group menu, each with its own left border and padding.
   The rail template now roots at `.mm-rail__in`, and editor's pick is a tinted
   card rather than a bordered gutter, so there is no rule left to double. */
.mm-rail__in{ display:block }
/* ONE ROW NOW, BECAUSE THERE IS ONLY ONE ROW OF THINGS (2026-09-16).
   This grid was two rows: the columns and the pick in row 1, the deals band
   spanning the full width of row 2, with the rail spanning both so the band sat
   under the columns rather than at the foot of a row the rail had made tall.
   `grid-auto-rows:min-content` came with it.

   The band is gone, and those three declarations went with it — but not before
   they broke the panel first. With nothing in row 2, `min-content` sized row 1
   to the columns' MINIMUM height (117px against 219px of links), so the third
   column's list painted straight through the first column's. A leftover from a
   deleted element is not inert; it is a rule still being obeyed for a reason
   that no longer exists. */
/* WHITE, AND DIVIDED BY A RULE (Lucky, 2026-09-16).
   The pick sat in a grey rounded card — "I don't like the backgrounds where you
   have the greys, the grey is a little bit dull. Keep it all white and make it
   more interesting in other ways." A tint is the laziest way to say "this is a
   different kind of thing"; a hairline says it without dulling the type, and
   what makes this column interesting is the red kicker, the headline at 1.42rem
   and the dated claim above it — facts and type, not a fill. */
.mj-nav .mm-rail{ border-left:1px solid var(--mj-rule); border-radius:0;
  padding:0 0 0 var(--sp-7); background:none;
  grid-column:2; grid-row:1; align-self:start }
.mj-nav .mm-inner{ row-gap:var(--sp-5) }
.mj-nav .mm-cols{ grid-column:1; grid-row:1 }
body.dark-mode .mj-nav .mm-rail{ background:none; border-left-color:#2a2a2d }

/* EDITOR'S PICK, given a front page of its own. */
.mj-nav .mm-rail-h{ display:flex; align-items:center; gap:8px; color:var(--mj-red);
  margin:0 0 var(--sp-4) }
.mj-nav .mm-rail-h::before{ content:""; width:18px; height:2px; background:var(--mj-red);
  flex:none }
.mj-nav .mm-feat{ display:block; padding-bottom:var(--sp-4) }
.mj-nav .mm-feat h3{ font-size:1.42rem; line-height:1.22; margin:10px 0 6px;
  text-wrap:balance }
.mj-nav .mm-feat p{ font-size:.92rem; line-height:1.55; color:var(--mj-text-muted);
  margin:0 }
.mj-nav .mm-feat:hover h3{ color:var(--mj-red) }
.mj-nav .mm-feat .thumb-chip{ background:var(--mj-charcoal); letter-spacing:1.4px }
body.dark-mode .mj-nav .mm-feat .thumb-chip{ background:#e9e9ec; color:#141416 }

/* A MINI WITH NO PICTURE SHOULD NOT SHOW AN EMPTY GRADIENT TILE. Two of them
   sat under the feature as blue-green blocks with nothing in them, which is the
   cheapest thing a menu can do. Where there is no image, the row becomes type:
   a rule, a kicker and a title that behaves like a headline. */
.mj-nav .mm-mini{ display:flex; gap:var(--sp-3); align-items:baseline;
  border-top:1px solid var(--mj-rule); padding:var(--sp-4) 0 0; margin-top:var(--sp-4) }
.mj-nav .mm-mini h5{ font-family:var(--font-display); font-weight:700; font-size:1rem;
  line-height:1.3; margin:0 0 3px }
.mj-nav .mm-mini:hover h5{ color:var(--mj-red) }


/* ── SMART SUMMARY · .mj-sum (c-6mlu, 2026-09-12) ─────────────────────────────
   Another article's quick take, closed until the reader wants it. Blue rule,
   because blue is navigation on this site and this is a way into another
   article; the reveal tray is red because it carries money. Closed, it is one
   calm bar: the question in the display face, a verb and a round control. */
.mj-sum{ margin:var(--sp-6, 28px) 0; background:var(--mj-surface); border:1px solid var(--mj-rule);
  border-left:4px solid var(--mj-trust); border-radius:0 var(--radius-md) var(--radius-md) 0 }
.mj-sum>summary, .mj-sum__bar{ list-style:none; cursor:pointer; display:grid; grid-template-columns:minmax(0,1fr) auto;
  column-gap:var(--sp-4, 16px); row-gap:4px; align-items:center; padding:var(--sp-4, 16px) var(--sp-5, 20px) }
.mj-sum>summary::-webkit-details-marker{ display:none }
.mj-sum>summary:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
.mj-sum__k{ grid-column:1; font-family:var(--font-meta); font-size:.64rem; font-weight:800;
  letter-spacing:1.6px; text-transform:uppercase; color:var(--mj-trust) }
.mj-sum__q{ grid-column:1; font-family:var(--font-display); font-weight:800; font-size:1.14rem;
  line-height:1.25; color:var(--mj-black) }
.mj-sum__hint{ grid-column:2; grid-row:1 / span 2; display:inline-flex; align-items:center; gap:10px;
  font-family:var(--font-meta); font-size:.64rem; font-weight:800; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-ink-900); white-space:nowrap }
.mj-sum__hint::before{ content:attr(data-closed) }
.mj-sum[open] .mj-sum__hint::before{ content:attr(data-open) }
.mj-sum__hint::after{ content:""; flex:none; width:30px; height:30px; border-radius:50%;
  background:var(--mj-trust) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") center / 15px no-repeat;
  transition:transform .2s ease }
.mj-sum[open] .mj-sum__hint::after{ transform:rotate(180deg) }
.mj-sum__body{ padding:0 var(--sp-5, 20px) var(--sp-5, 20px); border-top:1px solid var(--mj-rule) }
.mj-sum__a{ margin:var(--sp-4, 16px) 0; font-size:1.02rem; line-height:1.6; color:var(--mj-black) }
.mj-sum__pts{ list-style:none; margin:0 0 var(--sp-4, 16px); padding:0 }
.mj-sum__pt{ display:grid; grid-template-columns:minmax(130px, 30%) minmax(0,1fr); gap:14px;
  padding:11px 0; border-top:1px solid var(--mj-rule) }
.mj-sum__pl{ font-family:var(--font-display); font-weight:800; font-size:.82rem; line-height:1.35; color:var(--mj-black) }
.mj-sum__pv{ font-size:.93rem; line-height:1.55; color:var(--mj-text-muted) }
.mj-sum__sub{ margin:var(--sp-4, 16px) 0 }
.mj-sum__foot{ display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:8px 16px;
  margin:var(--sp-4, 16px) 0 0; padding-top:var(--sp-3, 12px); border-top:1px solid var(--mj-rule) }
.mj-sum__more{ font-family:var(--font-display); font-weight:800; font-size:.84rem; color:var(--mj-trust); text-decoration:none }
.mj-sum__more::after{ content:" \2192" }
.mj-sum__more:hover{ text-decoration:underline }
.mj-sum__v{ font-family:var(--font-meta); font-size:.62rem; letter-spacing:.4px; color:var(--mj-gray-400) }
body.dark-mode .mj-sum{ background:#141416; border-color:#2a2a2d; border-left-color:var(--mj-trust) }
body.dark-mode .mj-sum__q, body.dark-mode .mj-sum__a, body.dark-mode .mj-sum__pl{ color:#f2f2f5 }
body.dark-mode .mj-sum__hint{ color:#e7e7ea }
body.dark-mode .mj-sum__pv{ color:#a8a8ae }
body.dark-mode .mj-sum__body, body.dark-mode .mj-sum__pt, body.dark-mode .mj-sum__foot{ border-color:#2a2a2d }
@media (max-width:600px){
  .mj-sum>summary{ grid-template-columns:minmax(0,1fr) }
  .mj-sum__hint{ grid-column:1; grid-row:auto; margin-top:6px }
  .mj-sum__pt{ grid-template-columns:minmax(0,1fr); gap:2px }
}

/* ── DECISION LAYERS · the service area  (.mj-sa*)  ─────────────────────────────
   Lucky, 2026-09-16: "the product box or review block should reduce the amount of
   additional research a reader needs to do before deciding whether a
   product/company is relevant to them … For solar and renewable-energy companies,
   one of the obvious information gaps is service availability by location."

   ONE QUESTION, ANSWERED IN ONE GLANCE: "Can this company actually service me?"

   IT IS A LAYER, NOT A NEW BOX. It sits in the ranked card's left column — the
   space a physical product's photograph uses and an installer's card never
   filled — and in a review block as one more section. Same element in both, so a
   company's coverage looks the same wherever the company appears.

   THE MAP IS A TILE CARTOGRAM, NOT A MAP. One equal tile per province, placed
   roughly where it sits (site/locations.py owns the placement). A drawn map
   makes the Northern Cape most of the country and Gauteng a speck — the opposite
   of where installers are — and costs a network request for an outline the
   reader does not need.

   COLOUR FOLLOWS THE GRAMMAR. The vendor's colour fills what the VENDOR covers
   (identity); trust blue is orientation (the label, the reader's own tile);
   green is reserved for the one good answer; red appears nowhere, because
   nothing here is an action.

   FOUR HONEST STATES, and the fourth is silence: `national`, `regions`,
   `on_request` (the company does not publish an area — which is a real answer,
   and a different one from "no"), and no record at all, which renders nothing
   rather than guessing. */
.mj-sa{ --sa-tile:24px; display:grid; gap:10px; margin:0; padding:14px 0 0;
  border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-sa{ border-top-color:#2a2a2d }
.mj-sa__k{ margin:0; font-family:var(--font-display); font-weight:700; font-size:.74rem;
  letter-spacing:.2px; color:var(--mj-trust) }
.mj-sa__top{ display:grid; grid-template-columns:auto minmax(0,1fr); gap:12px; align-items:center }
.mj-sa__map{ display:grid; gap:3px;
  grid-template-columns:repeat(var(--sa-cols,3), var(--sa-tile));
  grid-template-rows:repeat(var(--sa-rows,4), calc(var(--sa-tile) * .8)) }
.mj-sa__t{ grid-column:var(--c, auto); grid-row:var(--r, auto); display:grid; place-items:center;
  font-family:var(--font-meta); font-size:.5rem; font-weight:700; letter-spacing:.2px; line-height:1;
  border:1px solid var(--mj-rule); border-radius:2px; background:var(--mj-surface);
  color:var(--mj-gray-400); transition:background-color .2s ease, box-shadow .2s ease }
body.dark-mode .mj-sa__t{ background:#131315; border-color:#2a2a2d; color:#6d6d74 }
.mj-sa__t[data-on="1"]{ background:var(--brand-accent, var(--mj-ink-900));
  border-color:var(--brand-accent, var(--mj-ink-900)); color:var(--brand-ink, #fff) }
/* part of a province — a named metro, not the whole region. Hatched in the
   vendor's colour, so "some of it" never reads as "all of it". */
.mj-sa__t[data-on="p"]{ color:var(--mj-ink); border-color:var(--brand-accent, var(--mj-ink-900));
  background:repeating-linear-gradient(135deg,
    color-mix(in srgb, var(--brand-accent, #0a0a0a) 42%, transparent) 0 3px, transparent 3px 6px) }
body.dark-mode .mj-sa__t[data-on="p"]{ color:#f0f0f2 }
/* not published: the outline is dashed because the answer is not "no" — it is
   "not said". Every tile is equally unknown, and the map must not imply more. */
.mj-sa[data-sa-status="on_request"] .mj-sa__t{ border-style:dashed }
/* the reader's own province, once chosen */
.mj-sa__t.is-pick{ box-shadow:0 0 0 2px var(--mj-surface), 0 0 0 3.5px var(--mj-trust) }
body.dark-mode .mj-sa__t.is-pick{ box-shadow:0 0 0 2px #0c0c0e, 0 0 0 3.5px var(--mj-trust) }
.mj-sa__txt{ min-width:0 }
.mj-sa__head{ margin:0; font-family:var(--font-display); font-weight:800; font-size:.98rem;
  line-height:1.18; color:var(--mj-black); text-wrap:balance }
body.dark-mode .mj-sa__head{ color:#fff }
.mj-sa__sub{ margin:4px 0 0; font-size:.76rem; line-height:1.4; color:var(--mj-text-muted) }
body.dark-mode .mj-sa__sub{ color:#a8a8ae }
.mj-sa__q{ display:grid; gap:8px }
.mj-sa__check{ display:grid; gap:5px; margin:0 }
.mj-sa__checkl{ font-family:var(--font-meta); font-size:.58rem; letter-spacing:1.2px;
  text-transform:uppercase; color:var(--mj-gray-400) }
.mj-sa__pick{ width:100%; margin:0; padding:7px 28px 7px 9px; font:inherit; font-size:.82rem;
  color:var(--mj-ink); background-color:var(--mj-surface); cursor:pointer;
  border:1px solid var(--mj-rule); border-radius:2px; appearance:none; -webkit-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23777' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 8px center; background-size:12px }
body.dark-mode .mj-sa__pick{ background-color:#131315; border-color:#2a2a2d; color:#e8e8ea }
.mj-sa__pick:hover{ border-color:var(--mj-gray-300) }
.mj-sa__pick:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:1px }
/* the answer. Hidden until the reader has asked, so nothing is claimed about a
   province nobody chose. */
.mj-sa__ans{ position:relative; margin:0; padding-left:17px; font-size:.8rem; line-height:1.45;
  font-weight:600; color:var(--mj-ink) }
.mj-sa__ans:not([data-a]){ display:none }
.mj-sa__ans::before{ position:absolute; left:0; top:0; font-weight:800 }
.mj-sa__ans[data-a="yes"]{ color:var(--mj-growth-text) }
.mj-sa__ans[data-a="yes"]::before{ content:"\2713" }
.mj-sa__ans[data-a="partial"]::before{ content:"\25D0"; color:var(--brand-accent, var(--mj-ink)) }
.mj-sa__ans[data-a="no"]{ color:var(--mj-text-muted); font-weight:500 }
.mj-sa__ans[data-a="no"]::before{ content:"\2013" }
.mj-sa__ans[data-a="ask"]{ color:var(--mj-trust); font-weight:500 }
.mj-sa__ans[data-a="ask"]::before{ content:"?" }
body.dark-mode .mj-sa__ans{ color:#e8e8ea }
body.dark-mode .mj-sa__ans[data-a="no"]{ color:#a8a8ae }
.mj-sa__note{ margin:0; font-size:.76rem; line-height:1.45; color:var(--mj-text-muted) }
body.dark-mode .mj-sa__note{ color:#a8a8ae }
.mj-sa__src{ margin:0; font-family:var(--font-meta); font-size:.6rem; letter-spacing:.3px;
  line-height:1.5; color:var(--mj-gray-400) }
.mj-sa__srca{ color:inherit; text-decoration:none; border-bottom:1px dotted currentColor }
.mj-sa__srca:hover{ color:var(--mj-trust) }
.mj-sa__srcd{ white-space:nowrap }
/* the picker's options: classed so the template can repeat the real ones and
   keep the placeholder, which is not a province. */
.mj-sa__opt{ color:var(--mj-ink) }
.mj-sa__none{ color:var(--mj-gray-400) }

/* the wide form, for a review block: the map and its answer beside the
   question rather than under it. */
.mj-sa--wide{ --sa-tile:30px; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  column-gap:var(--sp-6); row-gap:12px; padding:var(--sp-5) 0 0 }
.mj-sa--wide .mj-sa__k, .mj-sa--wide .mj-sa__note, .mj-sa--wide .mj-sa__src{ grid-column:1 / -1 }
.mj-sa--wide .mj-sa__head{ font-size:1.12rem }
.mj-sa--wide .mj-sa__q{ align-content:center }
@media(max-width:560px){
  .mj-sa--wide{ grid-template-columns:minmax(0,1fr) }
}

/* ── THE RANKED CARD HOSTS BOTH NEW PARTS ──────────────────────────────────
   The layer sits under the name in the left column; the tray opens under the
   buttons, inside the card, so a reader converts where they decided. On a phone
   the left column stacks on top, so the layer comes before the body there too —
   which is also the order a reader asks the questions in: "can they reach me?"
   before "what does it cost?". */
.mj-pb__layer:empty{ display:none }
/* the review block's slot for the same layer, in its wide form */
.rb-f-area{ margin:var(--sp-5) 0 0 }
.rb-f-area:empty{ display:none }
.mj-pb--rank .mj-pb__img:empty{ display:none }
.mj-pb--rank .mj-ctatray{ margin-top:var(--sp-4) }

/* ── ICON SYSTEM · data-icon (Lucky, c-je4y, 2026-09-12) ────────────────────────
   Five marks, one meaning each, applied by `renderers._badge_icon` so the same
   reason gets the same mark in every vertical. Registered in
   systems/mashjoy-brand-tokens.md §Icons. Drawn as CSS masks over currentColor,
   so an icon always takes its badge's own ink in either theme, and there is no
   image request or font to load. Add a mark there FIRST, then here. */
[data-icon="trophy"]{ --mj-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 21h8M12 17v4M7 4h10v5a5 5 0 0 1-10 0z'/%3E%3Cpath d='M7 6H4.5a2.5 2.5 0 0 0 2.6 3.9M17 6h2.5a2.5 2.5 0 0 1-2.6 3.9'/%3E%3C/svg%3E") }
[data-icon="medal"]{ --mj-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3l3 6M16 3l-3 6'/%3E%3Ccircle cx='12' cy='15' r='6'/%3E%3Cpath d='M12 12.2l.9 1.8 2 .3-1.45 1.4.35 2-1.8-.95-1.8.95.35-2L9.1 14.3l2-.3z'/%3E%3C/svg%3E") }
[data-icon="flag"]{ --mj-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 22V3'/%3E%3Cpath d='M5 4h14l-3 4.5 3 4.5H5'/%3E%3C/svg%3E") }
[data-icon="fire"]{ --mj-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 22c3.9 0 7-2.9 7-6.8 0-2.6-1.4-4.6-2.7-6.2-.4 1.6-1.3 2.7-2.6 3-.1-3.3-1.3-6.3-4.2-8 .2 3.1-3.5 6.1-3.5 11.2C6 19.1 8.1 22 12 22z'/%3E%3C/svg%3E") }
[data-icon="tag"]{ --mj-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.6 13.4l-7.2 7.2a2 2 0 0 1-2.8 0L3 13V3h10l7.6 7.6a2 2 0 0 1 0 2.8z'/%3E%3Ccircle cx='7.5' cy='7.5' r='1.3'/%3E%3C/svg%3E") }
[data-icon]:not([data-icon=""]):not(.rank-num)::before,
.rank-num[data-icon]:not([data-icon=""]) em::before{
  content:""; display:inline-block; flex:none; width:1.25em; height:1.25em;
  margin-right:.38em; vertical-align:-.26em; background:currentColor;
  -webkit-mask:var(--mj-ico) center / contain no-repeat; mask:var(--mj-ico) center / contain no-repeat }
.mj-prail__badge[data-icon]:not([data-icon=""]),
.mj-pstrip__badge[data-icon]:not([data-icon=""]){ display:inline-flex; align-items:center }
/* THE SALE TAG ON EVERY LINE THAT NAMES A SAVING (c-je4y 3): the rail's deal
   note, a product box's destination line when a discount applies, and the
   review block's discount line. One mark, the same green the saving chip uses. */
.mj-prail__note[data-kind="deal"]::before,
.mj-pb__disclosure[data-kind="deal"]::before,
.rb-f-paynote::before{
  content:""; display:inline-block; flex:none; width:14px; height:14px; margin:0 6px 0 0;
  vertical-align:-2px; border-radius:0; background:var(--mj-growth-text);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.6 13.4l-7.2 7.2a2 2 0 0 1-2.8 0L3 13V3h10l7.6 7.6a2 2 0 0 1 0 2.8z'/%3E%3Ccircle cx='7.5' cy='7.5' r='1.3'/%3E%3C/svg%3E") center / contain no-repeat; mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.6 13.4l-7.2 7.2a2 2 0 0 1-2.8 0L3 13V3h10l7.6 7.6a2 2 0 0 1 0 2.8z'/%3E%3Ccircle cx='7.5' cy='7.5' r='1.3'/%3E%3C/svg%3E") center / contain no-repeat }
.mj-prail__note[data-kind="deal"]::before{ margin-top:1px }
.mj-pb__disclosure[data-kind="deal"]{ color:var(--mj-black); font-weight:600 }
body.dark-mode .mj-pb__disclosure[data-kind="deal"]{ color:#e7e7ea }

/* ── THE PICK STAYS PUT, BESIDE THE ROW, NOT OVER IT (c-je4y 1, second pass) ─────
   The first pin made the pick `position:sticky` inside the scroller, so the card
   scrolling away slid UNDER it and showed as a sliver on both sides of it (Lucky:
   "the one that goes underneath the first element shouldn't show that it is
   underneath"). Now the script lifts the pick out of the scroller into a row
   beside it, and the scroller holds exactly two cards: nothing passes under
   anything, and every step moves one whole card. */
.mj-prail__row{ display:flex; gap:var(--rail-gap); align-items:stretch }
.mj-prail__row > .mj-prail__card{ flex:0 0 var(--rail-card) }
.mj-prail__row > .mj-prail__track{ flex:1 1 auto; min-width:0 }

/* ── THE FINDER IN TRUST BLUE (Lucky, c-f5t8, 2026-09-12) ─────────────────────
   "design a special version of the tool suitable for this niche, using a colour
   that has more to do with money or trust. I'd say the mashjoy.com blue … but the
   CTA sections should always be the preferred black or red." `tone: trust` on the
   payload. The questions, the kicker and the chosen answer turn blue; the buttons
   that act are left exactly as they are. */
.mj-finder[data-tone="trust"]{ border-top:3px solid var(--mj-trust) }
.mj-finder[data-tone="trust"] .mj-finder__k{ color:var(--mj-trust) }
.mj-finder[data-tone="trust"] .mj-finder__k::before{ background:var(--mj-trust) }
.mj-finder[data-tone="trust"] .mj-finder__opt:hover{ border-color:var(--mj-trust); color:var(--mj-trust) }
.mj-finder[data-tone="trust"] .mj-finder__opt:focus-visible{ outline-color:var(--mj-trust) }
.mj-finder[data-tone="trust"] .mj-finder__opt[aria-pressed="true"]{ background:var(--mj-trust); border-color:var(--mj-trust); color:#fff }
body.dark-mode .mj-finder[data-tone="trust"] .mj-finder__opt[aria-pressed="true"]{ background:#6b9bff; border-color:#6b9bff; color:#0b1020 }

/* THE TRUST TONE HAS TO WIN, IN BOTH THEMES (2026-09-12). The first version lost
   to the base rule's red top border, to `body.dark-mode .mj-finder__result`, and
   to the red question numbers and result kicker: measured on the invoicing page,
   the tool was blue only in its kicker chip. `html body` lifts every override
   above the dark-mode rules without !important. The answered result turns blue;
   its lead button turns black, because a button that acts stays black or red. */
html body .mj-finder[data-tone="trust"],
html body.dark-mode .mj-finder[data-tone="trust"]{ border-top:3px solid var(--mj-trust) }
html body .mj-finder[data-tone="trust"] .mj-finder__qn,
html body .mj-finder[data-tone="trust"] .mj-finder__rk{ color:var(--mj-trust) }
html body.dark-mode .mj-finder[data-tone="trust"] .mj-finder__qn,
html body.dark-mode .mj-finder[data-tone="trust"] .mj-finder__rk{ color:#8fb4ff }
html body .mj-finder[data-tone="trust"] .mj-finder__result,
html body.dark-mode .mj-finder[data-tone="trust"] .mj-finder__result{ border-top-color:var(--mj-trust) }
html body .mj-finder[data-tone="trust"] .mj-finder__result[data-state="answered"],
html body.dark-mode .mj-finder[data-tone="trust"] .mj-finder__result[data-state="answered"]{ background:var(--mj-trust) }
html body .mj-finder[data-tone="trust"] .mj-finder__result[data-state="answered"] .mj-finder__rk,
html body.dark-mode .mj-finder[data-tone="trust"] .mj-finder__result[data-state="answered"] .mj-finder__rk{ color:rgba(255,255,255,.85) }
html body .mj-finder[data-tone="trust"] .mj-finder__result[data-state="answered"] .mj-finder__cta[data-role="lead"],
html body.dark-mode .mj-finder[data-tone="trust"] .mj-finder__result[data-state="answered"] .mj-finder__cta[data-role="lead"]{
  background:var(--mj-ink-900); border-color:var(--mj-ink-900); color:#fff }
html body .mj-finder[data-tone="trust"] .mj-finder__result[data-state="answered"] .mj-finder__cta[data-role="lead"]:hover{
  background:var(--mj-red); border-color:var(--mj-red); color:#fff }

/* THE GROWTH TONE — each vertical's finder in its own colour (Lucky, c-gvx7, 2026-09-17):
   "when we have different articles from different verticals, we sometimes also have a
   specific color that is more in line with the treatment for that vertical, and that was
   not done." Blue (`trust`) is money-and-protection: invoicing and insurance. Green
   (`growth`) is energy and savings: renewable energy. Card machines keep the house
   red and black. Built on --mj-growth-text, the dark green, because the answered panel
   carries white text and the bright fill green would not read. Buttons that act stay
   black or red, as the trust tone's rule says. Default by vertical: renderers.FINDER_TONE. */
.mj-finder[data-tone="growth"]{ border-top:3px solid var(--mj-growth-text) }
.mj-finder[data-tone="growth"] .mj-finder__k{ color:var(--mj-growth-text) }
.mj-finder[data-tone="growth"] .mj-finder__k::before{ background:var(--mj-growth-text) }
.mj-finder[data-tone="growth"] .mj-finder__opt:hover{ border-color:var(--mj-growth-text); color:var(--mj-growth-text) }
.mj-finder[data-tone="growth"] .mj-finder__opt:focus-visible{ outline-color:var(--mj-growth-text) }
.mj-finder[data-tone="growth"] .mj-finder__opt[aria-pressed="true"]{ background:var(--mj-growth-text); border-color:var(--mj-growth-text); color:#fff }
body.dark-mode .mj-finder[data-tone="growth"] .mj-finder__opt[aria-pressed="true"]{ background:#3ee0a6; border-color:#3ee0a6; color:#04130d }

html body .mj-finder[data-tone="growth"],
html body.dark-mode .mj-finder[data-tone="growth"]{ border-top:3px solid var(--mj-growth-text) }
html body .mj-finder[data-tone="growth"] .mj-finder__qn,
html body .mj-finder[data-tone="growth"] .mj-finder__rk{ color:var(--mj-growth-text) }
html body.dark-mode .mj-finder[data-tone="growth"] .mj-finder__qn,
html body.dark-mode .mj-finder[data-tone="growth"] .mj-finder__rk{ color:#6ee7b7 }
html body .mj-finder[data-tone="growth"] .mj-finder__result,
html body.dark-mode .mj-finder[data-tone="growth"] .mj-finder__result{ border-top-color:var(--mj-growth-text) }
html body .mj-finder[data-tone="growth"] .mj-finder__result[data-state="answered"],
html body.dark-mode .mj-finder[data-tone="growth"] .mj-finder__result[data-state="answered"]{ background:var(--mj-growth-text) }
html body .mj-finder[data-tone="growth"] .mj-finder__result[data-state="answered"] .mj-finder__rk,
html body.dark-mode .mj-finder[data-tone="growth"] .mj-finder__result[data-state="answered"] .mj-finder__rk{ color:rgba(255,255,255,.85) }
html body .mj-finder[data-tone="growth"] .mj-finder__result[data-state="answered"] .mj-finder__cta[data-role="lead"],
html body.dark-mode .mj-finder[data-tone="growth"] .mj-finder__result[data-state="answered"] .mj-finder__cta[data-role="lead"]{
  background:var(--mj-ink-900); border-color:var(--mj-ink-900); color:#fff }
html body .mj-finder[data-tone="growth"] .mj-finder__result[data-state="answered"] .mj-finder__cta[data-role="lead"]:hover{
  background:var(--mj-red); border-color:var(--mj-red); color:#fff }


/* THE LOGO ON A DARK GROUND IS "logo-dark.svg": RED SQUARE, WHITE M, WHITE TEXT
   (Lucky, 2026-09-17, as a rule). This block used to invert the mark in dark mode —
   a white square with a red M, the pack's "dark main" — so the header in dark mode
   wore a different logo from the footer, which embeds logo-dark.svg verbatim.
   The square stays brand red and the M stays white in EVERY theme; only the
   wordmark and the .com dot follow the ground, and on dark they are white.
   Source: brand kit …/mashjoy.com logo pack/horizontal/mashjoy.com logo-dark.svg */
.mj-logo-svg.on-dark .mj-logo__icon,
body.dark-mode .mj-logo-svg .mj-logo__icon{ fill:#ec2227 }
.mj-logo-svg.on-dark .mj-logo__m,
body.dark-mode .mj-logo-svg .mj-logo__m{ fill:#fff }
.mj-logo-svg.on-dark,
body.dark-mode .mj-logo-svg{ color:#fff }
.mj-logo-svg.on-dark .mj-logo__dot,
body.dark-mode .mj-logo-svg .mj-logo__dot{ fill:#fff }

/* THE PICK'S BOX IS STILL ITS VENDOR'S BOX (Lucky, 2026-09-12: "update the border
   colours to match the brands"). `.is-pick` painted a green outline and a green rank
   square over the brand, so solar's #1 read as a different element from every other
   ranked box. The pick is already marked by its trophy; the frame follows the brand. */
.mj-pb--rank.is-pick[style*="--brand-accent"]{ border-color:var(--brand-accent);
  box-shadow:0 0 0 1px var(--brand-accent), 0 8px 24px rgba(10,10,10,.08) }
.mj-pb--rank.is-pick[style*="--brand-accent"] .rank-num i,
body.dark-mode .mj-pb--rank.is-pick[style*="--brand-accent"] .rank-num i{ background:var(--brand-accent); color:var(--brand-ink) }

/* ── WIDE ONLY WHEN THE CONTENT NEEDS IT (Lucky, 2026-09-13) ─────────────────────
   "Make them wide whenever the width is necessary to accommodate the content in
   full. If the tables can read well in default content width, keep them that way."
   The decision is MEASURED in the browser (inserts.js fitWide): a table, a recap or
   a short-answer table that overflows the column at content width gets its wide
   class; one that fits keeps the column. Measured on 2026-09-13: solar's recaps
   overflowed by 44px and the invoicing short-answer table by 76px; every card-
   machine table fitted. Server-side estimates stay as the no-script fallback. */
@media(min-width:768px){ .mj-vstrip__panel.is-wide.is-wide--table{ --sa-out:clamp(0px, calc((100vw - 728px) / 2), 140px) } }

/* A BREAKOUT INSIDE THE QUICK COMPARISON IS A BUG, as it is inside a panel: the fee
   scale centred on the summary's box and ran past its right edge. And no framed
   element inside a framed element (Lucky: "too many bordered elements inside each
   other … starts feeling like an AI tool vs normal editorial content"). */
.mj-sum .mj-wide{ width:100%; max-width:100%; margin-left:0; transform:none }
.mj-sum__sub .mj-fscale, .mj-sum__vtool .mj-finder{ border:0; border-radius:0; box-shadow:none;
  background:transparent; max-width:none; margin:var(--sp-3) 0 0 }
.mj-sum__sub .mj-fscale .mj-cta__bar{ background:transparent; color:var(--mj-black); padding-left:0; padding-right:0;
  border-bottom:1px solid var(--mj-rule) }
body.dark-mode .mj-sum__sub .mj-fscale .mj-cta__bar{ color:#f2f2f5; border-bottom-color:#2a2a2d }

/* ── THE QUICK COMPARISON, SECOND PASS (Lucky, 2026-09-13) ─────────────────────── */
.mj-sum__pl{ display:flex; align-items:flex-start; gap:10px; font-size:1rem }
.mj-sum__pn{ flex:none; margin-top:1px }
.mj-sum__plt{ min-width:0 }
.mj-sum__lead{ margin:var(--sp-5, 24px) 0 var(--sp-2); font-size:1rem; line-height:1.6; color:var(--mj-black) }
.mj-sum__verdict{ margin-top:var(--sp-5, 24px); padding-top:var(--sp-4, 16px); border-top:2px solid var(--mj-black) }
.mj-sum__vh{ margin:0 0 var(--sp-2); font-family:var(--font-display); font-weight:800; font-size:1.2rem; color:var(--mj-black) }
.mj-sum__vt{ margin:0; font-size:1rem; line-height:1.6; color:var(--mj-black) }
body.dark-mode .mj-sum__lead, body.dark-mode .mj-sum__vh, body.dark-mode .mj-sum__vt{ color:#f2f2f5 }
body.dark-mode .mj-sum__verdict{ border-top-color:#f2f2f5 }

/* ── THE RANKED PRODUCT BOX, ONE STANDARD (Lucky, 2026-09-13) ─────────────────────
   The Yoco Counter box, with his change: "the name appears to the left in
   consistency with review blocks so we can gain a bit of height reduction", and the
   repeated eyebrow removed. So: rank and reason, then the name and its checked stamp,
   in the left column; the description, one spec list, one price element and two
   balanced buttons on the right. No empty image frame. */
/* THE NAME SITS DIRECTLY UNDER THE RANK (2026-09-13). The rank line kept its 24px
   bottom margin from when it stood alone, and the title added 16px on top of the
   column's 12px gap: 52px of air between "8 · Rental, with a printer" and the name
   it belongs to. Measured, then closed to the column's own gap. */
.mj-pb--rank .rank-col .rank-num{ margin-bottom:0 }
/* THE CHECKED STAMP HEADS THE RIGHT COLUMN (Lucky, 2026-09-13): "the Checked box is
   not looking nice in the position it is placed … place it to the right of the rank
   text, then the heading over the description under it." It sits on the rank line's
   own row (the same 34px height, so the two share a centre line), the name keeps
   the left column to itself, and the description gets a named heading. */
.mj-pb--rank .mj-pb__bodyhead{ display:flex; align-items:center; min-height:34px; margin:0 0 14px }
/* "WHO IT'S FOR" IS A PROMISE, NOT A FIELD LABEL (Lucky, 2026-09-16).
   "Improve the text that says 'Who is it for' so that it can be written in
   Technor and also be able to stand out. Maybe give it a mashjoy-suitable
   colour so that people can easily read it."
   In the meta face at .66rem it was indistinguishable from `Monthly fee` — the
   one line that tells a reader whether to keep reading, set like a table
   header. The display face gives it weight; TRUST BLUE is the site's colour for
   "this is us orienting you", which is exactly what the line does. Red would
   have said act, green would have said saving; neither is true here. */
.mj-pb--rank .mj-pb__hooklabel{ margin:0 0 6px; font-family:var(--font-display);
  font-size:.82rem; font-weight:800; letter-spacing:.4px; text-transform:uppercase;
  color:var(--mj-trust) }
body.dark-mode .mj-pb--rank .mj-pb__hooklabel{ color:#8fb4ff }
.mj-pb--rank .mj-pb__hooklabel + .mj-pb__hook{ margin-top:0 }
.mj-pb--rank .rank-col .mj-pb__titlerow{ display:flex; flex-direction:column; align-items:flex-start; gap:8px; margin:0 }
.mj-pb--rank .rank-col .mj-pb__name{ font-size:1.45rem; line-height:1.15; margin:0 }
.mj-pb--rank .mj-pb__img:not(:has(img)){ display:none }
.mj-pb--rank .mj-pb__body > .mj-pb__hook:first-child{ margin-top:0 }
.mj-pb--rank .mj-pb__actions{ display:grid; grid-template-columns:1fr 1fr; gap:12px; align-items:stretch }
.mj-pb--rank .mj-pb__actions > :not(.mj-pb__cta){ grid-column:1 / -1 }
/* THE DESTINATION LINE RUNS THE FULL WIDTH (Lucky, c-0voo, 2026-09-20): "the text: you'll
   go to www.ikhokha.com's sign-up page ... ends up being wider from where it starts to the
   right edge." `.mj-pb__close` is display:contents, so its small print is a grid item too,
   and the rule above only reached the grid's own children: the line sat in the first
   button's column, three lines deep. */
.mj-pb--rank .mj-pb__close > :not(.mj-pb__cta){ grid-column:1 / -1 }
.mj-pb--rank .mj-pb__actions .mj-pb__cta{ width:100%; min-height:52px; white-space:nowrap; padding-left:10px; padding-right:10px }
.mj-pb__pay-tag:empty{ display:none }
@media(max-width:640px){ .mj-pb--rank .mj-pb__actions{ grid-template-columns:1fr } }

/* Phase 9 (2026-09-13): the recap's name column is slotted (#27), and review-block badges carry icons (#21). */
.mj-recap__nh{ white-space:nowrap }
.scms-rb-badge[data-icon]:not([data-icon=""]){ display:inline-flex; align-items:center }

/* ── 2026-09-16 · COLOUR IS IDENTITY, TOOLS ARE TOOLS (Lucky's four design notes) ──

   SLIM ROWS take their vendor's colour top and bottom and on the button; the
   recommended row takes the house red. The frame rule already read
   --brand-accent; the button did not, so three Yoco rows had yellow frames and
   red buttons — the wrong brand AND no brand. */
.mj-pb.mj-pb--slim[style*="--brand-accent"] .mj-pb__cta{ background:var(--brand-accent);
  border-color:var(--brand-accent); color:var(--brand-ink) }
.mj-pb.mj-pb--slim[style*="--brand-accent"] .mj-pb__cta:hover{
  background:color-mix(in srgb, var(--brand-accent) 82%, #000); color:var(--brand-ink) }
.mj-pb.mj-pb--slim.is-pick{ border-top-color:var(--mj-red); border-bottom-color:var(--mj-red) }
.mj-pb.mj-pb--slim.is-pick .mj-pb__cta{ background:var(--mj-red); border-color:var(--mj-red); color:#fff }

/* TWO SIDE BY SIDE, FINISHED THE WAY THE FIVE ARE. The pick: red rule top and
   bottom and the house red button. The other: its vendor's colour on both rules
   and on its button. The outlined grey button said "secondary" and nothing about
   who it was for. */
.mj-pstrip__card.is-pick{ box-shadow:none; border-top:3px solid var(--mj-red); border-bottom:3px solid var(--mj-red) }
.mj-pstrip__card:not(.is-pick)[style*="--brand-accent"]{ border-top:3px solid var(--brand-accent);
  border-bottom:3px solid var(--brand-accent) }
.mj-pstrip__card:not(.is-pick)[style*="--brand-accent"] .mj-pstrip__cta,
body.dark-mode .mj-pstrip__card:not(.is-pick)[style*="--brand-accent"] .mj-pstrip__cta{
  background:var(--brand-accent); border-color:var(--brand-accent); color:var(--brand-ink) }
.mj-pstrip__card:not(.is-pick)[style*="--brand-accent"] .mj-pstrip__cta:hover,
body.dark-mode .mj-pstrip__card:not(.is-pick)[style*="--brand-accent"] .mj-pstrip__cta:hover{
  background:color-mix(in srgb, var(--brand-accent) 82%, #000); border-color:color-mix(in srgb, var(--brand-accent) 82%, #000);
  color:var(--brand-ink) }

/* THE QUICK COMPARISON. The point label sits level with its verdict rather than
   hanging from the top of a three-line answer. */
.mj-sum__pt{ align-items:center }
.mj-sum__pl{ align-items:center }
.mj-sum__pn{ margin-top:0 }
/* A nested tool is announced, ruled off and headed at full weight, so the reader
   can tell what to read from what to operate. */
.mj-sum__tool{ margin:var(--sp-3, 12px) 0 0; padding-top:var(--sp-4, 16px); border-top:3px solid var(--mj-trust) }
.mj-sum__pill, .mj-sum .mj-finder[data-layout="embed"] .mj-finder__k{ display:inline-flex; align-items:center; gap:7px;
  margin:0 0 var(--sp-3, 12px); padding:4px 10px; border-radius:999px;
  font-family:var(--font-meta); font-size:.62rem; font-weight:800; letter-spacing:1.2px; text-transform:uppercase;
  color:var(--mj-trust); background:rgba(37,99,235,.08) }
.mj-sum__pill::before, .mj-sum .mj-finder[data-layout="embed"] .mj-finder__k::before{ content:""; width:7px; height:7px;
  flex:0 0 auto; border-radius:50%; background:var(--mj-trust) }
.mj-sum__tool .mj-fscale .mj-cta__bar{ font-size:1rem; letter-spacing:.6px; border-bottom:0; padding-top:0 }
.mj-sum__vtool{ margin-top:var(--sp-5, 24px); padding-top:var(--sp-4, 16px); border-top:3px solid var(--mj-trust) }
.mj-sum__vtool .mj-finder[data-layout="embed"] .mj-finder__h{ font-size:1.15rem }
body.dark-mode .mj-sum__pill, body.dark-mode .mj-sum .mj-finder[data-layout="embed"] .mj-finder__k{
  background:rgba(37,99,235,.18); color:#8fb4ff }

/* THE COUNTRY, SHOWN (Lucky, 2026-09-16): "where they install in South Africa, and
   maybe include the South African flag". Drawn from the panel's own country code,
   so a layer for another country gets its own flag, not ours. */
.mj-sa[data-sa-country="za"] .mj-sa__k{ display:flex; align-items:center; gap:8px }
.mj-sa[data-sa-country="za"] .mj-sa__k::before{ content:""; flex:none; width:22px; height:15px; border-radius:2px;
  box-shadow:0 0 0 1px rgba(0,0,0,.12);
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 900 600'%3E%3CclipPath id='c'%3E%3Crect width='900' height='600'/%3E%3C/clipPath%3E%3Cpath fill='%23E03C31' d='M0 0h900v300H0z'/%3E%3Cpath fill='%23001489' d='M0 300h900v300H0z'/%3E%3Cg clip-path='url(%23c)' fill='none'%3E%3Cpath d='M0 0l450 300L0 600M450 300h450' stroke='%23fff' stroke-width='200'/%3E%3Cpath d='M0 0l450 300L0 600M450 300h450' stroke='%23007749' stroke-width='120'/%3E%3C/g%3E%3Cpath fill='%23FFB81C' d='M0 100l300 200L0 500z'/%3E%3Cpath fill='%23000' d='M0 160l210 140L0 440z'/%3E%3C/svg%3E") center/cover no-repeat }

/* THE PICK IS MARKED BY ITS TROPHY AND ITS RULES, NOT A FOURTH AND FIFTH BORDER
   (Lucky, 2026-09-16: "the first one … even has four borders, which is not
   something that we have as part of our prototypes"). A 1px ring in the brand
   colour sat outside the top and bottom rules and drew a frame no other ranked
   box has. */
.mj-pb--rank.is-pick[style*="--brand-accent"]{ box-shadow:0 8px 24px rgba(10,10,10,.08) }

/* THE COMPANY'S MARK UNDER ITS NAME (Lucky, 2026-09-16, the solar ranking): "in
   their specific product boxes, also include the logo on the left side under the
   company name." Where a box has no product shot — an installer sells a service,
   not a boxed thing — the left column carries the vendor's logo instead, at a
   mark's size rather than a product photograph's. */
.mj-pb--rank .mj-pb__img[data-kind="logo"]{ width:auto; max-width:160px; height:auto; aspect-ratio:auto;
  margin-top:var(--sp-3, 12px); padding:10px 14px; background:#fff; border:1px solid var(--mj-rule);
  border-radius:var(--radius-sm); display:flex; align-items:center; justify-content:center }
.mj-pb--rank .mj-pb__img[data-kind="logo"] img{ width:auto; height:auto; max-width:132px; max-height:48px; object-fit:contain; padding:0 }
.mj-pb--rank .mj-pb__img[data-kind="logo"]::after{ display:none }
/* A LIGHT LOGO GETS A DARK WELL (J-2, 2026-10-01). Some vendors publish only a white mark
   (WiSolar, adumo); on the white well it was an empty bordered box. The build names these
   files `<brand>--logo--white.*` and draws them here. */
.mj-pb--rank .mj-pb__img[data-kind="logo-light"]{ width:auto; max-width:160px; height:auto; aspect-ratio:auto;
  margin-top:var(--sp-3, 12px); padding:10px 14px; background:#141416; border:1px solid #141416;
  border-radius:var(--radius-sm); display:flex; align-items:center; justify-content:center }
.mj-pb--rank .mj-pb__img[data-kind="logo-light"] img{ width:auto; height:auto; max-width:132px; max-height:48px; object-fit:contain; padding:0 }
.mj-pb--rank .mj-pb__img[data-kind="logo-light"]::after{ display:none }

/* THE ACTION COLUMN (Lucky, c-46q1, 2026-09-16). A table that decides a purchase
   carries the button on the row it decided, to the right, in the house red. */
.mj-tw__cell.mj-tw__act{ text-align:right; white-space:nowrap; vertical-align:middle }
.mj-tw__rowcta{ display:inline-flex; align-items:center; justify-content:center; padding:9px 14px;
  border-radius:var(--radius-sm); background:var(--mj-red); color:#fff; text-decoration:none;
  font-family:var(--font-display); font-weight:800; font-size:.74rem; letter-spacing:.6px; text-transform:uppercase }
.mj-tw__rowcta:hover{ background:#b3181c; color:#fff }
@media(max-width:620px){ .mj-tw__cell.mj-tw__act{ text-align:left } }

/* ── LEAD FORMS, 2026-09-16 ─────────────────────────────────────────────────
   A free-text answer, the honest preview line, an inline tray under its
   paragraph, and a sale flash that looks like one. */
.mj-lead__f--wide{ grid-column:1 / -1 }
.mj-lead__ta{ min-height:84px; resize:vertical; font:inherit; line-height:1.5 }
.mj-lead__preview{ margin:var(--sp-3) 0 0; padding:8px 12px; border-radius:var(--radius-sm);
  font-family:var(--font-meta); font-size:.66rem; letter-spacing:.6px; text-transform:uppercase;
  color:#8a5a00; background:#fff4d6 }
.mj-lead.is-previewed .mj-lead__preview{ color:#fff; background:#b3181c }
body.dark-mode .mj-lead__preview{ color:#ffd98a; background:#2b2211 }
.mj-ctatray--inline{ margin:var(--sp-3) 0 var(--sp-5) }
.mj-pb__pay-tag, .mj-prail__tag{ display:inline-flex; align-items:center; gap:5px }
.mj-pb__pay-tag::before{ content:""; flex:none; width:11px; height:11px; background:currentColor;
  -webkit-mask:var(--mj-ico-tag) center/contain no-repeat; mask:var(--mj-ico-tag) center/contain no-repeat }
:root{ --mj-ico-tag:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.6 13.4l-7.2 7.2a2 2 0 0 1-2.8 0L3 13V3h10l7.6 7.6a2 2 0 0 1 0 2.8z'/%3E%3Ccircle cx='7.5' cy='7.5' r='1.3'/%3E%3C/svg%3E") }

/* The option class a lead form's select carries from its prototype (2026-09-16). */
.lead-opt{ font:inherit }

/* ── LEAD-CAPTURE BOXES AND THE SERVICE-AREA PANEL, 2026-09-16 ───────────────
   Lucky: "the pricing should show the price and then say view more … and then at
   the bottom, our standard CTA element with a black background." Plus the panel:
   two lines, the flag above, bigger, ticks, a better picker, no vendor link. */
.mj-pb--rank .mj-pb__close{ display:contents }
.mj-pb--rank .mj-pb__actions[data-mode="lead"] .mj-pb__close{ display:grid; grid-column:1 / -1; grid-template-columns:1fr;
  gap:10px; margin-top:var(--sp-2, 8px); padding:var(--sp-4, 16px) var(--sp-5, 20px) var(--sp-5, 20px);
  background:#121214; color:#f2f2f2; border-radius:var(--radius-sm); border-top:3px solid var(--brand-accent, var(--mj-red)) }
.mj-pb__ctahead{ margin:0; font-family:var(--font-display); font-weight:800; font-size:1.05rem; color:#fff }
.mj-pb__promise{ margin:0 0 4px; font-size:.9rem; line-height:1.5; color:#c9c9cf }
.mj-pb--rank .mj-pb__actions[data-mode="lead"] .mj-pb__close .mj-pb__cta{ width:100%; min-height:52px }
.mj-pb--rank .mj-pb__actions[data-mode="lead"] .mj-pb__close .mj-pb__cta--ghost{ background:transparent; color:#fff; border-color:#4a4a52 }
.mj-pb__plans{ grid-column:1 / -1; border-top:1px solid var(--mj-rule); border-bottom:1px solid var(--mj-rule); padding:8px 0 }
/* THE TRAY THAT ENDS THE BOX SITS ON ITS EDGE (J-2b, 2026-10-01). With nothing after it
   (no lead form, no where-to-buy), the wrapper's bottom rule and padding plus the box's own
   28px drew a blank white strip under every software tray: a frame inside a frame. */
.mj-pb--rank:has(> .mj-pb__plans:last-child){ padding-bottom:0 }
.mj-pb--rank > .mj-pb__plans:last-child{ border-bottom:0; padding-bottom:0 }
.mj-pb__planssum{ list-style:none; cursor:pointer; display:flex; align-items:center; gap:8px;
  font-family:var(--font-display); font-weight:800; font-size:.8rem; letter-spacing:.4px; color:var(--mj-black) }
.mj-pb__planssum::-webkit-details-marker{ display:none }
.mj-pb__planssum::after{ content:""; width:8px; height:8px; margin-left:auto; border-right:2px solid currentColor;
  border-bottom:2px solid currentColor; transform:rotate(45deg); transition:transform .2s }
.mj-pb__plans[open] .mj-pb__planssum::after{ transform:rotate(-135deg) }
.mj-pb__planst{ width:100%; border-collapse:collapse; margin:10px 0 6px; font-size:.84rem }
.mj-pb__planst td{ padding:8px 6px; border-top:1px solid var(--mj-rule); vertical-align:top }
.mj-pb__plan-n{ font-family:var(--font-display); font-weight:800; white-space:nowrap }
.mj-pb__plan-p{ font-weight:700; white-space:nowrap }
.mj-pb__plan-d{ color:var(--mj-text-muted) }
.mj-pb__plansv{ margin:4px 0 0; font-family:var(--font-meta); font-size:.62rem; letter-spacing:.4px; color:var(--mj-gray-400) }
body.dark-mode .mj-pb__planssum{ color:#f2f2f5 }
body.dark-mode .mj-pb__plans, body.dark-mode .mj-pb__planst td{ border-color:#2a2a2d }
@media(max-width:560px){ .mj-pb__planst tr{ display:grid; grid-template-columns:1fr auto } .mj-pb__plan-d{ grid-column:1 / -1; border-top:0 !important; padding-top:0 } }

.mj-sa__k{ flex-direction:column; align-items:flex-start !important; gap:4px !important; font-size:.95rem !important; line-height:1.2 }
.mj-sa__k1, .mj-sa__k2{ display:block }
.mj-sa__k2{ font-weight:600; color:var(--mj-text-muted) }
.mj-sa[data-sa-country="za"] .mj-sa__k::before{ width:30px !important; height:20px !important; margin-bottom:4px }
.mj-sa__head{ display:flex; align-items:center; gap:7px }
.mj-sa[data-sa-status="national"] .mj-sa__head::before, .mj-sa[data-sa-status="regions"] .mj-sa__head::before{
  content:""; flex:none; width:18px; height:18px; border-radius:50%; background:var(--mj-growth, #10b981) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.2 4.2L19 7'/%3E%3C/svg%3E") center/12px no-repeat }
.mj-sa[data-sa-status="on_request"] .mj-sa__head::before{ content:"?"; flex:none; width:18px; height:18px; border-radius:50%;
  display:inline-flex; align-items:center; justify-content:center; font-size:.7rem; font-weight:800; color:#fff; background:var(--mj-trust) }
.mj-sa__checkl{ display:block; margin-bottom:6px; font-family:var(--font-display) !important; font-weight:800 !important;
  font-size:.78rem !important; letter-spacing:.3px !important; text-transform:none !important; color:var(--mj-black) !important }
.mj-sa__pick{ appearance:none; -webkit-appearance:none; width:100%; padding:11px 38px 11px 14px; border:1.5px solid var(--mj-rule);
  border-radius:var(--radius-sm); background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23171718' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") right 12px center/14px no-repeat;
  font:inherit; font-size:.92rem; color:var(--mj-black); cursor:pointer; transition:border-color .15s, box-shadow .15s }
.mj-sa__pick:hover{ border-color:var(--mj-black) }
.mj-sa__pick:focus-visible{ outline:none; border-color:var(--mj-trust); box-shadow:0 0 0 3px rgba(37,99,235,.18) }
body.dark-mode .mj-sa__pick{ background-color:#141416; color:#f2f2f5; border-color:#2a2a2d }
body.dark-mode .mj-sa__checkl{ color:#f2f2f5 !important }

/* ── THE COUNT, THE PICKER AND THE ANSWER, GIVEN SOME LIFE (Lucky, 2026-09-17) ──
   "Make the number of provinces text smaller and maybe also add a bit of colour to
   those text elements so that they can feel interesting and a bit interactive …
   fix the [picker] so that it can also have a bit of a design taste, and the check
   your province text."
   · THE COUNT is a badge in the vendor's colour, and the words beside it drop a size:
     the number is the fact, "of 9 provinces" is its unit.
   · THE QUESTION is trust blue with a location pin — blue is this site's colour for
     finding your way, and the pin says what the control is before it is read.
   · THE PICKER is tinted blue until used, and once the reader has chosen it turns
     white with a blue edge, so the panel visibly holds their answer.
   · THE ANSWER is a small coloured callout per state — green for yes, the vendor's
     tint for partly, blue for "confirm with them", grey for not listed — instead of
     a line of text that looked like any other. Red stays out: nothing here is an
     action. */
.mj-sa__head{ font-size:.82rem !important; font-weight:700; gap:8px !important; line-height:1.25 }
.mj-sa__n{ flex:none; display:inline-grid; place-items:center; min-width:28px; height:28px; padding:0 7px;
  border-radius:14px; background:var(--brand-accent, var(--mj-ink-900)); color:var(--brand-ink, #fff);
  font-family:var(--font-display); font-weight:800; font-size:.92rem; line-height:1;
  box-shadow:0 0 0 3px color-mix(in srgb, var(--brand-accent, #0a0a0a) 20%, transparent) }
.mj-sa__of{ font-family:var(--font-display); font-weight:700; color:var(--mj-black) }
body.dark-mode .mj-sa__of{ color:#f2f2f5 }
/* a badge replaces the tick: two marks saying one thing is one too many */
.mj-sa__head:has(.mj-sa__n)::before{ display:none !important }

.mj-sa__checkl{ display:flex !important; align-items:center; gap:6px; color:var(--mj-trust) !important }
.mj-sa__checkl::before{ content:""; flex:none; width:15px; height:15px; background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a7 7 0 0 0-7 7c0 5.25 7 13 7 13s7-7.75 7-13a7 7 0 0 0-7-7zm0 9.6a2.6 2.6 0 1 1 0-5.2 2.6 2.6 0 0 1 0 5.2z'/%3E%3C/svg%3E") center/contain no-repeat; mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a7 7 0 0 0-7 7c0 5.25 7 13 7 13s7-7.75 7-13a7 7 0 0 0-7-7zm0 9.6a2.6 2.6 0 1 1 0-5.2 2.6 2.6 0 0 1 0 5.2z'/%3E%3C/svg%3E") center/contain no-repeat }
body.dark-mode .mj-sa__checkl{ color:#8fb4ff !important }

.mj-sa__pick{ font-family:var(--font-display); font-weight:600; font-size:.88rem !important;
  border-color:color-mix(in srgb, var(--mj-trust) 32%, var(--mj-rule)) !important;
  background-color:var(--mj-trust-subtle) !important;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232563EB' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") !important; background-position:right 12px center !important;
  background-size:14px !important; background-repeat:no-repeat !important }
.mj-sa__pick:hover{ border-color:var(--mj-trust) !important; background-color:#fff !important }
.mj-sa:has(.mj-sa__ans[data-a]) .mj-sa__pick{ background-color:#fff !important;
  border-color:var(--mj-trust) !important; box-shadow:inset 3px 0 0 var(--mj-trust) }
body.dark-mode .mj-sa__pick{ background-color:#101a2e !important; border-color:#27406e !important }
body.dark-mode .mj-sa:has(.mj-sa__ans[data-a]) .mj-sa__pick{ background-color:#141416 !important }

.mj-sa__ans[data-a]{ padding:9px 11px 9px 34px; border-radius:var(--radius-md); font-size:.8rem;
  line-height:1.45; animation:mj-sa-in .22s ease both }
.mj-sa__ans[data-a]::before{ left:11px !important; top:9px !important; width:16px; height:16px; border-radius:50%;
  display:grid; place-items:center; font-size:.62rem; line-height:1; color:#fff }
.mj-sa__ans[data-a="yes"]{ background:var(--mj-growth-subtle); color:var(--mj-growth-text) }
.mj-sa__ans[data-a="yes"]::before{ background:var(--mj-growth) }
.mj-sa__ans[data-a="partial"]{ background:color-mix(in srgb, var(--brand-accent, #0a0a0a) 12%, #fff); color:var(--mj-ink) }
.mj-sa__ans[data-a="partial"]::before{ background:var(--brand-accent, var(--mj-ink-900)); color:var(--brand-ink, #fff) !important }
.mj-sa__ans[data-a="ask"]{ background:var(--mj-trust-subtle); color:var(--mj-trust) }
.mj-sa__ans[data-a="ask"]::before{ background:var(--mj-trust) }
.mj-sa__ans[data-a="no"]{ background:var(--mj-surface-2); color:var(--mj-text-muted) }
.mj-sa__ans[data-a="no"]::before{ background:var(--mj-gray-400) }
body.dark-mode .mj-sa__ans[data-a]{ background:#18181b }
@keyframes mj-sa-in{ from{ opacity:0; transform:translateY(-3px) } to{ opacity:1; transform:none } }
@media(prefers-reduced-motion:reduce){ .mj-sa__ans[data-a]{ animation:none } }

/* ── PLANS AND PRICES · the band and its three layouts  (.mj-pp*)  ─────────────────
   Lucky, 2026-09-17: make "view all plans" "wider … below everything … give it a green
   or maybe the color of the logo … give it an icon so that that section also stands
   out", and turn vendors' sales tables into our own summaries and reusable tables.
   Prototype and reasoning: mashjoy-decision-layers.html, "Plans and prices".

   THE BAND is full width under both card columns and bleeds to the card's edges, like
   the quote band below it — so it is a strip of the card, not a box inside one. It
   wears the VENDOR'S colour: a light tint, and an icon tile in the vendor's own
   readable ink. The closed band already answers "how many, what kind, from what". */
.mj-pb--rank > .mj-pb__plans{ grid-column:1 / -1; margin:6px -28px 0 }
.mj-pp{ --pp-a:var(--brand-accent, var(--mj-growth)); --pp-i:var(--brand-ink, #fff);
  background:color-mix(in srgb, var(--pp-a) 8%, #fff);
  border-top:1px solid color-mix(in srgb, var(--pp-a) 45%, #fff);
  border-bottom:1px solid color-mix(in srgb, var(--pp-a) 45%, #fff) }
body.dark-mode .mj-pp{ background:color-mix(in srgb, var(--pp-a) 10%, #131315);
  border-color:color-mix(in srgb, var(--pp-a) 35%, #131315) }
.mj-pp__sum{ list-style:none; cursor:pointer; display:grid;
  grid-template-columns:auto minmax(0,1fr) auto auto; align-items:center; gap:14px;
  padding:14px 28px; transition:background-color .15s ease }
.mj-pp__sum::-webkit-details-marker{ display:none }
.mj-pp__sum:hover{ background:color-mix(in srgb, var(--pp-a) 14%, #fff) }
body.dark-mode .mj-pp__sum:hover{ background:color-mix(in srgb, var(--pp-a) 16%, #131315) }
.mj-pp__sum:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:-2px }
.mj-pp__icon{ width:40px; height:40px; border-radius:var(--radius-md); background:var(--pp-a);
  display:grid; place-items:center }
.mj-pp__icon::before{ content:""; width:22px; height:22px; background:var(--pp-i);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='3' width='16' height='18' rx='2'/%3E%3Cpath d='M8 8h5M8 12h8M8 16h6'/%3E%3Ccircle cx='16.5' cy='8' r='1.3' fill='black'/%3E%3C/svg%3E") center/contain no-repeat; mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='3' width='16' height='18' rx='2'/%3E%3Cpath d='M8 8h5M8 12h8M8 16h6'/%3E%3Ccircle cx='16.5' cy='8' r='1.3' fill='black'/%3E%3C/svg%3E") center/contain no-repeat }
.mj-pp__txt{ min-width:0; display:grid; gap:2px }
.mj-pp__t{ font-family:var(--font-display); font-weight:800; font-size:1rem; line-height:1.2; color:var(--mj-black) }
.mj-pp__s{ font-size:.82rem; line-height:1.4; color:var(--mj-text-muted) }
body.dark-mode .mj-pp__t{ color:#fff }
body.dark-mode .mj-pp__s{ color:#b4b4ba }
.mj-pp__from{ display:grid; justify-items:end; line-height:1.1; white-space:nowrap }
.mj-pp__fromk, .mj-pp__fromu{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:.8px;
  text-transform:uppercase; color:var(--mj-gray-400) }
.mj-pp__fromv{ font-family:var(--font-display); font-weight:800; font-size:1.2rem; color:var(--mj-black) }
body.dark-mode .mj-pp__fromv{ color:#fff }
.mj-pp__cue{ width:30px; height:30px; border-radius:50%; background:var(--mj-black); position:relative;
  transition:transform .2s ease }
.mj-pp__cue::before{ content:""; position:absolute; left:50%; top:45%; width:8px; height:8px;
  border-right:2px solid #fff; border-bottom:2px solid #fff; transform:translate(-50%,-50%) rotate(45deg) }
.mj-pp[open] .mj-pp__cue{ transform:rotate(180deg) }
body.dark-mode .mj-pp__cue{ background:#fff }
body.dark-mode .mj-pp__cue::before{ border-color:#0c0c0e }
.mj-pp__body{ padding:4px 28px 6px }
.mj-pp__note{ margin:0; padding:10px 28px 14px; font-family:var(--font-meta); font-size:.64rem;
  letter-spacing:.3px; line-height:1.5; color:var(--mj-gray-400) }

/* CARDS — ways to pay. Each plan is a white card with the vendor's rule on top:
   the price, what you get (ticked) and the terms you sign up to, in that order. */
.mj-pp__cards{ display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:12px }
/* THREE PLANS SIT IN ONE ROW (J-2b, 2026-10-01). Software usually sells three; at 220px
   the third card dropped alone onto a second row, half a tray of empty space. */
.mj-pp__cards:has(> :nth-child(3):last-child){ grid-template-columns:repeat(auto-fit,minmax(176px,1fr)) }
.mj-pp__card{ background:#fff; border-top:3px solid var(--pp-a); border-radius:0 0 var(--radius-md) var(--radius-md);
  padding:14px 16px 16px; box-shadow:0 1px 0 color-mix(in srgb, var(--pp-a) 30%, var(--mj-rule)) }
body.dark-mode .mj-pp__card{ background:#18181b }
.mj-pp__cn{ margin:0; font-family:var(--font-display); font-weight:800; font-size:.98rem; color:var(--mj-black) }
.mj-pp__cp{ margin:6px 0 0; display:flex; align-items:baseline; gap:6px; flex-wrap:wrap }
.mj-pp__cpv{ font-family:var(--font-display); font-weight:800; font-size:1.35rem; letter-spacing:-.01em; color:var(--mj-black) }
.mj-pp__cpu{ font-size:.8rem; color:var(--mj-text-muted) }
.mj-pp__calt{ margin:2px 0 0; font-size:.8rem; color:var(--mj-text-muted) }
.mj-pp__ckinc, .mj-pp__ckterm{ margin:12px 0 5px; font-family:var(--font-meta); font-size:.58rem;
  letter-spacing:1.1px; text-transform:uppercase; color:var(--mj-gray-400) }
.mj-pp__inc, .mj-pp__terms{ list-style:none; margin:0; padding:0; display:grid; gap:4px }
.mj-pp__i{ position:relative; padding-left:22px; font-size:.86rem; line-height:1.4; color:var(--mj-ink) }
.mj-pp__i::before{ content:""; position:absolute; left:0; top:2px; width:15px; height:15px; border-radius:50%;
  background:var(--mj-growth) }
.mj-pp__i::after{ content:""; position:absolute; left:3px; top:5px; width:9px; height:9px; background:#fff;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.2 4.2L19 7'/%3E%3C/svg%3E") center/contain no-repeat; mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.2 4.2L19 7'/%3E%3C/svg%3E") center/contain no-repeat }
.mj-pp__tm{ position:relative; padding-left:22px; font-size:.82rem; line-height:1.4; color:var(--mj-text-muted) }
.mj-pp__tm::before{ content:""; position:absolute; left:5px; top:.62em; width:6px; height:6px; border-radius:50%;
  background:var(--mj-gray-300) }
.mj-pp__cnote{ margin:10px 0 0; padding-top:8px; border-top:1px dashed var(--mj-rule); font-size:.8rem; color:var(--mj-text-muted) }
body.dark-mode .mj-pp__cn, body.dark-mode .mj-pp__cpv{ color:#fff }
body.dark-mode .mj-pp__i{ color:#e8e8ea }

/* LADDER — sizes for a need. One row per size, smallest first: the size as a chip in
   the vendor's colour, who it fits, and the price on the right where the eye lands. */
.mj-pp__ladder{ list-style:none; margin:0; padding:0; display:grid; gap:6px }
.mj-pp__step{ display:grid; grid-template-columns:minmax(64px,auto) minmax(0,1fr) auto; align-items:center;
  gap:14px; background:#fff; border-radius:var(--radius-md); padding:10px 14px;
  border-left:3px solid var(--pp-a); transition:transform .15s ease, box-shadow .15s ease }
.mj-pp__step:hover{ transform:translateX(2px); box-shadow:0 2px 10px rgba(10,10,10,.06) }
body.dark-mode .mj-pp__step{ background:#18181b }
.mj-pp__sz{ justify-self:start; padding:5px 9px; border-radius:var(--radius-md); background:var(--pp-a); color:var(--pp-i);
  font-family:var(--font-display); font-weight:800; font-size:.78rem; letter-spacing:.2px; white-space:nowrap }
.mj-pp__fit{ min-width:0; display:grid; gap:1px }
.mj-pp__fitt{ font-family:var(--font-display); font-weight:700; font-size:.88rem; color:var(--mj-black) }
.mj-pp__det{ font-size:.8rem; color:var(--mj-text-muted) }
.mj-pp__lp{ display:flex; align-items:baseline; gap:5px; white-space:nowrap }
.mj-pp__lpv{ font-family:var(--font-display); font-weight:800; font-size:1.08rem; color:var(--mj-black) }
.mj-pp__lpu{ font-size:.74rem; color:var(--mj-text-muted) }
body.dark-mode .mj-pp__fitt, body.dark-mode .mj-pp__lpv{ color:#fff }

/* TABLE — a spec catalogue. Shared columns so kits compare down the page, family rows
   between groups, and the price column set apart on the right. Scrolls inside itself
   on a phone, never the page. */
.mj-pp__scroll{ overflow-x:auto; background:#fff; border-radius:var(--radius-md) }
body.dark-mode .mj-pp__scroll{ background:#18181b }
.mj-pp__mx{ width:100%; min-width:520px; border-collapse:collapse; font-size:.86rem }
.mj-pp__mxh{ text-align:left; padding:10px 12px; font-family:var(--font-meta); font-size:.6rem; letter-spacing:1px;
  text-transform:uppercase; color:var(--mj-gray-400); border-bottom:2px solid var(--pp-a) }
.mj-pp__mxh:last-child, .mj-pp__mxp{ text-align:right }
.mj-pp__mxhead{ background:#fff }
body.dark-mode .mj-pp__mxhead{ background:#18181b }
.mj-pp__mxbody{ vertical-align:top }
.mj-pp__mxg + .mj-pp__mxr > *{ border-top:0 }
.mj-pp__mxgl{ text-align:left; padding:12px 12px 6px; font-family:var(--font-display); font-weight:800; font-size:.8rem;
  color:var(--mj-black); background:color-mix(in srgb, var(--pp-a) 7%, #fff) }
.mj-pp__mxn{ text-align:left; padding:10px 12px; font-family:var(--font-display); font-weight:800; white-space:nowrap; color:var(--mj-black) }
.mj-pp__mxc{ padding:10px 12px; color:var(--mj-ink) }
.mj-pp__mxp{ padding:10px 12px; white-space:nowrap }
.mj-pp__mxpv{ font-family:var(--font-display); font-weight:800; color:var(--mj-black) }
.mj-pp__mxpu{ display:block; font-size:.7rem; color:var(--mj-text-muted) }
.mj-pp__mxr > *{ border-top:1px solid var(--mj-rule) }
.mj-pp__mxr:hover > *{ background:color-mix(in srgb, var(--pp-a) 6%, #fff) }
.mj-pp__mxc[data-empty]{ color:var(--mj-gray-400); font-style:italic }
body.dark-mode .mj-pp__mxgl{ background:#1f1f23; color:#fff }
body.dark-mode .mj-pp__mxn, body.dark-mode .mj-pp__mxpv{ color:#fff }
body.dark-mode .mj-pp__mxc{ color:#e8e8ea }
body.dark-mode .mj-pp__mxr > *{ border-top-color:#2a2a2d }

/* The price list and the quote band are two strips of one card, so they meet:
   without this the card's 28px grid gap left a white stripe between them. */
.mj-pb--rank > .mj-pb__wtb:empty{ display:none }
.mj-pb--rank > .mj-pb__plans:has(+ .mj-pb__foot),
.mj-pb--rank > .mj-pb__plans:has(+ .mj-pb__wtb:empty + .mj-pb__foot){ margin-bottom:-28px }
.mj-pb--rank > .mj-pb__plans + .mj-pb__foot,
.mj-pb--rank > .mj-pb__plans + .mj-pb__wtb:empty + .mj-pb__foot{ margin-top:0 }
@media(max-width:720px){
  .mj-pb--rank > .mj-pb__plans{ margin:6px calc(-1 * var(--sp-5)) 0 }
  .mj-pb--rank > .mj-pb__plans:has(+ .mj-pb__foot),
  .mj-pb--rank > .mj-pb__plans:has(+ .mj-pb__wtb:empty + .mj-pb__foot){ margin-bottom:calc(-1 * var(--sp-4)) }
  .mj-pp__sum{ grid-template-columns:auto minmax(0,1fr) auto; padding:12px var(--sp-5) }
  .mj-pp__from{ grid-column:2; grid-row:2; justify-items:start; display:flex; align-items:baseline; gap:5px }
  .mj-pp__cue{ grid-column:3; grid-row:1 / span 2 }
  .mj-pp__body{ padding:4px var(--sp-5) 6px }
  .mj-pp__note{ padding:10px var(--sp-5) 14px }
  .mj-pp__step{ grid-template-columns:minmax(0,1fr) auto; row-gap:6px }
  .mj-pp__fit{ grid-column:1 / -1; grid-row:2 }
}

/* ── THE LEAD FOOT, THE VENDOR'S FORM, AND THE THANK-YOU (Lucky, 2026-09-17) ─────────
   Full width, edge to edge; the form wears the company's colour rather than ours;
   after sending, the element closes back to its band with a thank-you in it. */
.mj-pb--rank .mj-pb__foot{ grid-column:1 / -1; margin:6px -28px -28px; background:#121214; color:#f2f2f2;
  border-top:3px solid var(--brand-accent, var(--mj-red)) }
.mj-pb__footin{ display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center; gap:14px 28px; padding:22px 28px }
.mj-pb__foot .mj-pb__ctahead{ font-size:1.15rem }
.mj-pb__foot .mj-pb__promise{ margin:4px 0 0; max-width:62ch }
.mj-pb__footcta{ display:inline-flex; align-items:center; justify-content:center; min-height:52px; padding:0 26px;
  border-radius:var(--radius-sm); background:var(--brand-accent, var(--mj-red)); color:var(--brand-ink, #fff);
  font-family:var(--font-display); font-weight:800; font-size:.82rem; letter-spacing:.6px; text-transform:uppercase;
  text-decoration:none; white-space:nowrap }
.mj-pb__footcta:hover{ filter:brightness(.92) }
.mj-pb__thanks{ grid-column:1 / -1; margin:0; display:flex; align-items:flex-start; gap:12px; font-size:.98rem; line-height:1.55; color:#fff }
.mj-pb__thanks::before, .mj-lead-thanks::before{ content:""; flex:none; width:26px; height:26px; border-radius:50%;
  background:var(--mj-growth, #10b981) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.2 4.2L19 7'/%3E%3C/svg%3E") center/15px no-repeat }
.mj-pb--rank .mj-pb__foot .mj-ctatray{ border-top:0; background:#fff; border-radius:0 0 var(--radius-md) var(--radius-md) }
body.dark-mode .mj-pb--rank .mj-pb__foot .mj-ctatray{ background:#161618 }
.mj-pb--rank .mj-pb__foot .mj-ctatray .mj-lead{ max-width:none; margin:0 }
/* 720, NOT 640: the card drops to 24px padding at 720px, so a 28px bleed between
   641 and 720 overhung its edges by 4px (2026-09-17). */
@media(max-width:720px){
  .mj-pb--rank .mj-pb__foot{ margin:6px calc(-1 * var(--sp-5)) calc(-1 * var(--sp-5)) }
  .mj-pb__footin{ grid-template-columns:1fr; padding:18px var(--sp-5) }
  .mj-pb__footcta{ width:100% }
}
.mj-lead-thanks{ display:flex; align-items:flex-start; gap:12px; margin:var(--sp-3) 0 var(--sp-5); padding:14px 18px;
  border-left:3px solid var(--brand-accent, var(--mj-growth)); background:#f3f8f5; font-size:.96rem; line-height:1.55 }
body.dark-mode .mj-lead-thanks{ background:#132019 }

.mj-lead[style*="--brand-accent"] .mj-lead__head{ background:linear-gradient(180deg, color-mix(in srgb, var(--brand-accent) 13%, #ffffff), #ffffff) }
.mj-lead[style*="--brand-accent"] .mj-lead__k{ color:color-mix(in srgb, var(--brand-accent) 62%, #101014);
  background:color-mix(in srgb, var(--brand-accent) 14%, #ffffff); border-color:color-mix(in srgb, var(--brand-accent) 35%, #ffffff) }
.mj-lead[style*="--brand-accent"] .mj-lead__k::before{ background:var(--brand-accent) }
.mj-lead[style*="--brand-accent"] .mj-lead__in:focus{ border-color:var(--brand-accent); outline:none;
  box-shadow:0 0 0 3px color-mix(in srgb, var(--brand-accent) 24%, transparent) }
.mj-lead[style*="--brand-accent"] .mj-lead__act{ background:color-mix(in srgb, var(--brand-accent) 9%, #ffffff);
  border-top:3px solid var(--brand-accent) }
.mj-lead[style*="--brand-accent"] .mj-lead__ctext, .mj-lead[style*="--brand-accent"] .mj-lead__ctext b{ color:#141416 }
.mj-lead[style*="--brand-accent"] .mj-lead__cnote, .mj-lead[style*="--brand-accent"] .mj-lead__note{ color:#4a4a52 }
.mj-lead[style*="--brand-accent"] .mj-lead__check{ accent-color:var(--brand-accent) }
.mj-lead[style*="--brand-accent"] .mj-lead__btn{ background:var(--brand-accent); color:var(--brand-ink); border-color:var(--brand-accent) }
.mj-lead[style*="--brand-accent"] .mj-lead__btn:hover{ filter:brightness(.92) }
body.dark-mode .mj-lead[style*="--brand-accent"] .mj-lead__head{ background:linear-gradient(180deg, color-mix(in srgb, var(--brand-accent) 18%, #161618), #161618) }
body.dark-mode .mj-lead[style*="--brand-accent"] .mj-lead__act{ background:color-mix(in srgb, var(--brand-accent) 12%, #161618) }
body.dark-mode .mj-lead[style*="--brand-accent"] .mj-lead__ctext, body.dark-mode .mj-lead[style*="--brand-accent"] .mj-lead__ctext b{ color:#f2f2f5 }
body.dark-mode .mj-lead[style*="--brand-accent"] .mj-lead__cnote, body.dark-mode .mj-lead[style*="--brand-accent"] .mj-lead__note{ color:#a8a8ae }
.mj-lead__offer{ display:inline-flex; align-items:center; gap:7px; margin:12px 0 0; padding:6px 12px; border-radius:999px;
  font-family:var(--font-meta); font-size:.7rem; font-weight:800; letter-spacing:.5px; text-transform:uppercase;
  color:var(--mj-growth-text, #0b6b45); background:var(--mj-growth-subtle, #e6f6ee) }
.mj-lead__offer::before{ content:""; width:12px; height:12px; background:currentColor;
  -webkit-mask:var(--mj-ico-tag) center/contain no-repeat; mask:var(--mj-ico-tag) center/contain no-repeat }
.mj-lead__err{ margin:0 0 10px; font-size:.86rem; font-weight:700; color:#b3181c }

.mj-pb__footcopy{ min-width:0 }

/* A hidden part of the lead foot stays hidden: display rules above must not override [hidden] (2026-09-17). */
.mj-pb__thanks[hidden], .mj-pb__footcta[hidden], .mj-pb__footcopy[hidden]{ display:none }

/* ── WHERE TO BUY IT · .mj-wtb (2026-09-17) ───────────────────────────────
   The same product at every South African store that sells it. A decision
   layer about a PRODUCT (systems/smartcms-decision-layers.md §6), drawn under
   both columns of the ranked card.

   ONE ROW, THREE JOBS, LEFT TO RIGHT: who sells it and on what terms, what it
   costs, where to go. The terms sit under the store's name because they are
   what makes a lower price mean something: a nine-day delivery, a six-month
   warranty, an EU plug. A price with its terms hidden is the listing's job,
   not ours.

   COLOUR GRAMMAR, as the service-area panel: trust blue labels, green only for
   "lowest today" (money saved), amber for a flag that changes the deal, no red
   — nothing in this panel is a warning about the reader. */
.mj-pb--rank > .mj-pb__wtb{ grid-column:1 / -1 }
.mj-wtb{ display:grid; gap:10px; margin:0; padding:16px 0 0; border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-wtb{ border-top-color:#2a2a2d }
.mj-wtb__k{ margin:0; display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 10px;
  font-family:var(--font-display); font-weight:700; font-size:.95rem; line-height:1.2; color:var(--mj-trust) }
/* The panel label's first line is the display face at the element's own size; the rule
   exists so the class is in the library (L0), not to restyle it. */
.mj-wtb__k1{ font-weight:700 }
.mj-wtb__k2{ font-weight:600; font-size:.8rem; color:var(--mj-text-muted) }
.mj-wtb__rows{ list-style:none; margin:0; padding:0; display:grid; gap:0 }
.mj-wtb__row{ display:grid; grid-template-columns:minmax(0,1fr) auto minmax(150px,auto); gap:6px 18px;
  align-items:center; padding:12px 0; border-top:1px solid var(--mj-rule) }
.mj-wtb__row:first-child{ border-top:0 }
body.dark-mode .mj-wtb__row{ border-top-color:#2a2a2d }
.mj-wtb__row[data-stock="out_of_stock"]{ opacity:.78 }
.mj-wtb__who{ min-width:0 }
.mj-wtb__store{ margin:0; font-family:var(--font-display); font-weight:800; font-size:.95rem; color:var(--mj-black) }
body.dark-mode .mj-wtb__store{ color:#fff }
.mj-wtb__meta{ margin:2px 0 0; font-family:var(--font-meta); font-size:.66rem; line-height:1.45;
  letter-spacing:.2px; color:var(--mj-text-muted) }
body.dark-mode .mj-wtb__meta{ color:#a8a8ae }
.mj-wtb__flag{ display:inline-block; margin:5px 0 0; padding:2px 8px; border-radius:999px;
  font-family:var(--font-meta); font-size:.6rem; font-weight:700; letter-spacing:.3px;
  color:#8a5a00; background:#fff4dc; border:1px solid #f1d49a }
.mj-wtb__flag[data-tone="note"]{ color:var(--mj-trust); background:var(--mj-trust-subtle); border-color:rgba(37,99,235,.28) }
body.dark-mode .mj-wtb__flag{ color:#f3c56b; background:rgba(243,197,107,.1); border-color:rgba(243,197,107,.3) }
body.dark-mode .mj-wtb__flag[data-tone="note"]{ color:#8fb4ff; background:rgba(37,99,235,.16); border-color:rgba(37,99,235,.34) }
.mj-wtb__price{ display:grid; justify-items:end; gap:1px; text-align:right }
.mj-wtb__best{ font-family:var(--font-meta); font-size:.58rem; font-weight:700; letter-spacing:.8px;
  text-transform:uppercase; color:var(--mj-growth, #0f7a3c) }
.mj-wtb__now{ font-family:var(--font-display); font-weight:800; font-size:1.15rem; line-height:1.1; color:var(--mj-black) }
body.dark-mode .mj-wtb__now{ color:#fff }
.mj-wtb__was{ font-family:var(--font-display); font-size:.78rem; color:var(--mj-gray-400) }
.mj-wtb__go{ display:grid; justify-items:stretch; gap:4px }
/* THE STORE'S DOOR (Lucky, 2026-09-17): the button wears the store's own colour,
   carried on the row as --brand-accent/--brand-ink (site.json brand_colors); an
   out-of-stock row keeps the colour as an outline, because it can only be looked at. */
.mj-wtb__cta{ display:flex; align-items:center; justify-content:center; padding:9px 12px; text-decoration:none;
  font-family:var(--font-display); font-weight:800; font-size:.68rem; letter-spacing:.3px; text-transform:uppercase;
  color:var(--brand-ink, #fff); background:var(--brand-accent, var(--mj-ink-900));
  border:1px solid var(--brand-accent, var(--mj-ink-900)); border-radius:var(--radius-sm); transition:filter .16s, background-color .16s }
.mj-wtb__cta:hover{ filter:brightness(.9) }
.mj-wtb__row[data-stock="out_of_stock"] .mj-wtb__cta{ background:transparent; color:var(--mj-ink-900) }
.mj-wtb__row[data-stock="out_of_stock"] .mj-wtb__cta:hover{ filter:none; background:color-mix(in srgb, var(--brand-accent, #000) 8%, #fff) }
body.dark-mode .mj-wtb__cta[style], body.dark-mode .mj-wtb__cta{ border-color:var(--brand-accent, #e7e7ea) }
body.dark-mode .mj-wtb__row[style*="#000000"] .mj-wtb__cta{ background:#e7e7ea; color:#141416; border-color:#e7e7ea }
body.dark-mode .mj-wtb__row[data-stock="out_of_stock"] .mj-wtb__cta{ background:transparent; color:#e7e7ea; border-color:#4a4a52 }
.mj-wtb__logo{ display:block; height:18px; width:auto; max-width:110px; margin:0 0 4px; object-fit:contain }
/* STOCK IS A PILL: mashjoy.com green in stock, a calm slate out of stock (never red,
   nothing here is the reader's fault), amber when it ships later. */
.mj-wtb__stock{ display:inline-block; margin:4px 0 0; padding:1px 8px; border-radius:999px;
  font-family:var(--font-meta); font-size:.6rem; font-weight:700; letter-spacing:.3px;
  color:#0b7a5b; background:var(--mj-growth-subtle, #e6f6ef); border:1px solid rgba(16,185,129,.35) }
.mj-wtb__stock:empty{ display:none }
.mj-wtb__row[data-stock="out_of_stock"] .mj-wtb__stock{ color:#55606e; background:#eef0f3; border-color:#d5dae1 }
.mj-wtb__row[data-stock="ships_later"] .mj-wtb__stock,
.mj-wtb__row[data-stock="preorder"] .mj-wtb__stock{ color:#8a5a00; background:#fff4dc; border-color:#f1d49a }
body.dark-mode .mj-wtb__stock{ color:#34d8a6; background:rgba(16,185,129,.1); border-color:rgba(16,185,129,.3) }
body.dark-mode .mj-wtb__row[data-stock="out_of_stock"] .mj-wtb__stock{ color:#a8b0bb; background:rgba(168,176,187,.1); border-color:rgba(168,176,187,.28) }
body.dark-mode .mj-wtb__row[data-stock="ships_later"] .mj-wtb__stock,
body.dark-mode .mj-wtb__row[data-stock="preorder"] .mj-wtb__stock{ color:#f3c56b; background:rgba(243,197,107,.1); border-color:rgba(243,197,107,.3) }
/* THE CHEAPEST PRICE IS ALWAYS GREEN. */
.mj-wtb__row[data-cheapest="1"] .mj-wtb__now{ color:var(--mj-growth, #0f7a3c) }
body.dark-mode .mj-wtb__row[data-cheapest="1"] .mj-wtb__now{ color:#34d8a6 }
/* A REASON TO BUY HERE, where the disclosure used to say what we do not earn. */
.mj-wtb__perk{ margin:0; display:flex; align-items:center; justify-content:center; gap:5px;
  font-size:.64rem; line-height:1.35; color:var(--mj-text-muted); text-align:center }
.mj-wtb__perk::before{ content:""; flex:none; width:11px; height:11px; background:var(--mj-growth, #0f7a3c);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.2 4.2L19 7'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.2 4.2L19 7'/%3E%3C/svg%3E") center/contain no-repeat }
body.dark-mode .mj-wtb__perk{ color:#b4b4ba }
/* An empty disclosure line (no relationship to state, see renderers._DISC) takes no space. */
.cta-disc:empty, .mj-pstrip__disclosure:empty, .mj-pb__disclosure:empty{ display:none }
.mj-wtb__foot{ margin:0; font-family:var(--font-meta); font-size:.62rem; letter-spacing:.3px; color:var(--mj-gray-400) }
@media(max-width:640px){
  .mj-wtb__row{ grid-template-columns:minmax(0,1fr) auto }
  .mj-wtb__go{ grid-column:1 / -1 }
}

/* ── WILL IT RUN YOUR HOME? · .mj-fit (2026-09-17, c-vskv) ────────────────
   A strip of the ranked card, like the plans band: under both columns, bleeding
   to the card's edges, in a light tint of the vendor's colour, closed by
   default. Closed, it states the one number no spec sheet prints: what a
   watt-hour costs at the lowest in-stock price. Open, the reader's check on the
   left and the way out (smaller, cheaper, bigger) on the right.
   COLOUR GRAMMAR: the vendor tints the strip; the answer is ink; "over its
   output" is amber, never red (nothing here is the reader's fault); the units
   that carry the load are trust blue links. */
.mj-pb--rank > .mj-pb__runs{ grid-column:1 / -1; margin:6px -28px 0 }
.mj-fit{ --fit-a:var(--brand-accent, var(--mj-growth)); --fit-i:var(--brand-ink, #fff);
  background:color-mix(in srgb, var(--fit-a) 6%, #fff);
  border-top:1px solid color-mix(in srgb, var(--fit-a) 40%, #fff);
  border-bottom:1px solid color-mix(in srgb, var(--fit-a) 40%, #fff) }
body.dark-mode .mj-fit{ background:color-mix(in srgb, var(--fit-a) 9%, #131315);
  border-color:color-mix(in srgb, var(--fit-a) 32%, #131315) }
.mj-fit__sum{ list-style:none; cursor:pointer; display:grid;
  grid-template-columns:auto minmax(0,1fr) auto auto; align-items:center; gap:14px;
  padding:14px 28px; transition:background-color .15s ease }
.mj-fit__sum::-webkit-details-marker{ display:none }
.mj-fit__sum:hover{ background:color-mix(in srgb, var(--fit-a) 12%, #fff) }
body.dark-mode .mj-fit__sum:hover{ background:color-mix(in srgb, var(--fit-a) 15%, #131315) }
.mj-fit__sum:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:-2px }
.mj-fit__icon{ width:40px; height:40px; border-radius:var(--radius-md); background:var(--fit-a); display:grid; place-items:center }
.mj-fit__icon::before{ content:""; width:22px; height:22px; background:var(--fit-i);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 11l9-7 9 7'/%3E%3Cpath d='M5 10v10h14V10'/%3E%3Cpath d='M12.5 11l-2.5 4h4l-2.5 4'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 11l9-7 9 7'/%3E%3Cpath d='M5 10v10h14V10'/%3E%3Cpath d='M12.5 11l-2.5 4h4l-2.5 4'/%3E%3C/svg%3E") center/contain no-repeat }
.mj-fit__txt{ min-width:0; display:grid; gap:2px }
.mj-fit__t{ font-family:var(--font-display); font-weight:800; font-size:1rem; line-height:1.2; color:var(--mj-black) }
.mj-fit__s{ font-size:.82rem; line-height:1.4; color:var(--mj-text-muted) }
body.dark-mode .mj-fit__t{ color:#fff }
body.dark-mode .mj-fit__s{ color:#b4b4ba }
.mj-fit__kf{ display:grid; justify-items:end; line-height:1.1; white-space:nowrap }
.mj-fit__kfv{ font-family:var(--font-display); font-weight:800; font-size:1.2rem; color:var(--mj-black) }
.mj-fit__kfu{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:.8px; text-transform:uppercase; color:var(--mj-gray-400) }
body.dark-mode .mj-fit__kfv{ color:#fff }
.mj-fit__cue{ width:30px; height:30px; border-radius:50%; background:var(--mj-black); position:relative; transition:transform .2s ease }
.mj-fit__cue::before{ content:""; position:absolute; left:50%; top:45%; width:8px; height:8px;
  border-right:2px solid #fff; border-bottom:2px solid #fff; transform:translate(-50%,-50%) rotate(45deg) }
.mj-fit[open] .mj-fit__cue{ transform:rotate(180deg) }
body.dark-mode .mj-fit__cue{ background:#fff }
body.dark-mode .mj-fit__cue::before{ border-color:#0c0c0e }
.mj-fit__body{ display:grid; grid-template-columns:minmax(0,1.1fr) minmax(0,1fr); gap:24px; padding:6px 28px 8px }
.mj-fit__calc, .mj-fit__alts{ min-width:0 }
.mj-fit__k, .mj-fit__dk, .mj-fit__uk, .mj-fit__carryk{ margin:0 0 6px; font-family:var(--font-meta); font-size:.58rem;
  font-weight:700; letter-spacing:1.1px; text-transform:uppercase; color:var(--mj-trust) }
.mj-fit__uk{ margin-top:16px }
body.dark-mode .mj-fit__k, body.dark-mode .mj-fit__dk, body.dark-mode .mj-fit__uk, body.dark-mode .mj-fit__carryk{ color:#8fb4ff }
.mj-fit__how{ margin:0 0 10px; font-size:.84rem; line-height:1.5; color:var(--mj-text-muted) }
body.dark-mode .mj-fit__how{ color:#b4b4ba }
/* J-5 (Lucky, 2.4, 2026-10-04): "make it easy for people to see that they can add the
   watts in those boxes … the final design should suggest adding numbers … in the
   placeholders, instead of just a W, add a number". So: a pill in the closed strip
   that says it is interactive; numbered steps; each appliance a label over a REAL
   field (a frame in the vendor's colour, a fixed W unit, and a faint "e.g. 100").
   THE EXAMPLE IS THE SAME IN EVERY ROW ON PURPOSE (landmine 8): one number repeated
   under a fridge and a router reads as "type a number like this", never as either
   appliance's rating. A placeholder is never a value, so it never enters the sum. */
.mj-fit__try{ display:inline-flex; align-items:center; gap:6px; width:max-content; max-width:100%; margin:0 0 3px; padding:3px 9px 3px 7px;
  border-radius:999px; background:var(--fit-a); color:var(--fit-i); font-family:var(--font-meta); font-size:.56rem; font-weight:700;
  letter-spacing:1px; text-transform:uppercase; line-height:1.3 }
.mj-fit__dot{ flex:none; width:7px; height:7px; border-radius:50%; background:var(--fit-i); animation:mj-fit-pulse 1.8s ease-out infinite }
@keyframes mj-fit-pulse{ 0%{ box-shadow:0 0 0 0 rgba(255,255,255,.7) } 70%{ box-shadow:0 0 0 7px rgba(255,255,255,0) } 100%{ box-shadow:0 0 0 0 rgba(255,255,255,0) } }
@media (prefers-reduced-motion:reduce){ .mj-fit__dot{ animation:none } }
.mj-fit[open] .mj-fit__try{ display:none }
.mj-fit__tryt, .mj-fit__k1, .mj-fit__k2{ font:inherit }
.mj-fit__sn{ display:inline-grid; place-items:center; width:17px; height:17px; margin-right:7px; border-radius:50%;
  background:var(--fit-a); color:var(--fit-i); font-size:.58rem; letter-spacing:0; vertical-align:1px }
.mj-fit__loads{ list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px 12px }
.mj-fit__load{ display:flex; flex-direction:column; gap:4px; min-width:0 }
.mj-fit__lname{ font-family:var(--font-display); font-weight:700; font-size:.84rem; line-height:1.3; color:var(--mj-ink) }
body.dark-mode .mj-fit__lname{ color:#e7e7ea }
.mj-fit__field{ position:relative; display:block }
.mj-fit__watts{ display:block; width:100%; box-sizing:border-box; height:44px; padding:0 34px 0 12px; font:inherit;
  font-family:var(--font-display); font-weight:800; font-size:1.08rem; text-align:left;
  border:1.5px solid color-mix(in srgb, var(--fit-a) 45%, var(--mj-rule)); border-radius:var(--radius-sm);
  background:#fff; color:var(--mj-black); cursor:text; -moz-appearance:textfield; appearance:textfield;
  transition:border-color .15s ease, box-shadow .15s ease }
.mj-fit__watts::-webkit-outer-spin-button, .mj-fit__watts::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0 }
.mj-fit__watts::placeholder{ font-weight:500; color:#a3a3ab; opacity:1 }
.mj-fit__watts:hover{ border-color:var(--fit-a) }
.mj-fit__watts:focus{ outline:none; border-color:var(--fit-a); box-shadow:0 0 0 3px color-mix(in srgb, var(--fit-a) 22%, transparent) }
.mj-fit__watts:not(:placeholder-shown){ border-color:var(--fit-a) }
body.dark-mode .mj-fit__watts{ background:#0c0c0e; border-color:color-mix(in srgb, var(--fit-a) 50%, #3a3a40); color:#fff }
body.dark-mode .mj-fit__watts::placeholder{ color:#6b6b73 }
body.dark-mode .mj-fit__watts:hover, body.dark-mode .mj-fit__watts:focus,
body.dark-mode .mj-fit__watts:not(:placeholder-shown){ border-color:color-mix(in srgb, var(--fit-a) 80%, #fff) }
.mj-fit__unit{ position:absolute; right:12px; top:50%; transform:translateY(-50%); font-family:var(--font-display);
  font-weight:800; font-size:.92rem; color:var(--mj-gray-400); pointer-events:none }
.mj-fit__watts:not(:placeholder-shown) + .mj-fit__unit{ color:var(--mj-black) }
body.dark-mode .mj-fit__watts:not(:placeholder-shown) + .mj-fit__unit{ color:#fff }
.mj-fit__ok{ margin-bottom:4px }
.mj-fit__meter{ height:6px; margin:8px 0 2px; border-radius:3px; overflow:hidden;
  background:color-mix(in srgb, var(--fit-a) 16%, var(--mj-rule)) }
body.dark-mode .mj-fit__meter{ background:#2a2a2d }
.mj-fit__bar{ display:block; height:100%; width:0; border-radius:3px; background:var(--fit-a); transition:width .25s ease }
.mj-fit[data-over="1"] .mj-fit__bar{ background:#d9951c }
.mj-fit__out{ margin:14px 0 0; padding:12px 14px; border-radius:var(--radius-md); background:#fff;
  border-left:3px solid var(--fit-a) }
body.dark-mode .mj-fit__out{ background:#18181b }
.mj-fit[data-over="1"] .mj-fit__out{ border-left-color:#d9951c }
.mj-fit__run{ margin:0; display:flex; align-items:baseline; gap:8px; flex-wrap:wrap }
.mj-fit__rlead{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:.8px; text-transform:uppercase; color:var(--mj-gray-400) }
.mj-fit__hours{ font-family:var(--font-display); font-weight:800; font-size:1.3rem; color:var(--mj-black) }
body.dark-mode .mj-fit__hours{ color:#fff }
.mj-fit[data-over="1"] .mj-fit__hours{ color:#8a5a00 }
body.dark-mode .mj-fit[data-over="1"] .mj-fit__hours{ color:#f3c56b }
.mj-fit__line, .mj-fit__surge{ margin:4px 0 0; font-size:.84rem; line-height:1.45; color:var(--mj-text-muted) }
body.dark-mode .mj-fit__line, body.dark-mode .mj-fit__surge{ color:#b4b4ba }
.mj-fit__surge{ color:#8a5a00 }
body.dark-mode .mj-fit__surge{ color:#f3c56b }
.mj-fit__carry{ margin:10px 0 0; padding:10px 0 0; border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-fit__carry{ border-top-color:#2a2a2d }
.mj-fit__clist{ list-style:none; margin:0; padding:0; display:grid; gap:6px }
.mj-fit__c{ display:flex; flex-wrap:wrap; justify-content:space-between; gap:2px 10px; font-size:.84rem }
.mj-fit__c[hidden]{ display:none }
.mj-fit__cn{ font-weight:700; color:var(--mj-trust); text-decoration:none }
.mj-fit__cn:hover{ text-decoration:underline }
.mj-fit__cm{ font-family:var(--font-meta); font-size:.66rem; color:var(--mj-text-muted) }
body.dark-mode .mj-fit__cn{ color:#8fb4ff }
.mj-fit__basis{ margin:10px 0 0; font-size:.74rem; line-height:1.5; color:var(--mj-gray-400) }
.mj-fit__down, .mj-fit__up{ min-width:0 }
.mj-fit__alist, .mj-fit__blist{ list-style:none; margin:0; padding:0; display:grid; gap:0 }
.mj-fit__alt{ display:grid; grid-template-columns:minmax(0,1fr) auto; gap:2px 12px; padding:10px 0;
  border-top:1px solid color-mix(in srgb, var(--fit-a) 22%, var(--mj-rule)) }
.mj-fit__alt:first-child{ border-top:0; padding-top:2px }
body.dark-mode .mj-fit__alt{ border-top-color:#2a2a2d }
.mj-fit__an{ font-family:var(--font-display); font-weight:800; font-size:.95rem; color:var(--mj-black); text-decoration:none }
.mj-fit__an::after{ content:" \2192"; color:var(--fit-a) }
.mj-fit__an:hover{ text-decoration:underline }
body.dark-mode .mj-fit__an{ color:#fff }
.mj-fit__ap{ display:flex; align-items:baseline; gap:5px; justify-self:end; white-space:nowrap }
.mj-fit__apv{ font-family:var(--font-display); font-weight:800; font-size:.98rem; color:var(--mj-black) }
body.dark-mode .mj-fit__apv{ color:#fff }
.mj-fit__aps{ font-family:var(--font-meta); font-size:.6rem; color:var(--mj-gray-400) }
.mj-fit__ar{ grid-column:1 / -1; font-size:.82rem; line-height:1.45; color:var(--mj-text-muted) }
body.dark-mode .mj-fit__ar{ color:#b4b4ba }
.mj-fit__note{ margin:0; padding:8px 28px 14px; font-family:var(--font-meta); font-size:.62rem; letter-spacing:.3px; line-height:1.5; color:var(--mj-gray-400) }
/* The strip meets the where-to-buy panel below it, which draws its own rule. */
.mj-pb--rank > .mj-pb__runs + .mj-pb__wtb > .mj-wtb{ border-top:0 }
@media(max-width:720px){
  .mj-pb--rank > .mj-pb__runs{ margin:6px calc(-1 * var(--sp-5)) 0 }
  .mj-fit__sum{ grid-template-columns:auto minmax(0,1fr) auto; padding:12px var(--sp-5) }
  .mj-fit__kf{ grid-column:2; grid-row:2; justify-items:start; display:flex; align-items:baseline; gap:5px }
  .mj-fit__cue{ grid-column:3; grid-row:1 / span 2 }
  .mj-fit__body{ grid-template-columns:1fr; gap:18px; padding:4px var(--sp-5) 6px }
  .mj-fit__note{ padding:8px var(--sp-5) 14px }
}


/* ── RUNTIME CALCULATOR · .mj-rtc (2026-09-17; rebuilt J-5, 2026-10-04) ──
   Built on the `.mj-cta` tool frame, like the fee scale, so a reader who has
   used one calculator on this site recognises the next.
   J-5 (Lucky, 2.5): "boost the appearance of this tool so that it also feels a
   bit interactive, add a bubble that makes people know it's an interactive tool
   above the instructions, boost the watt fields … make the power station
   selectors cards … add brands as filters". So the tool now reads as three
   numbered steps: (1) the reader's watts in tiles whose fields look like fields
   (a frame, a unit, a faint example number), (2) the power stations as cards
   with their shot, specs and lowest in-stock price, filtered by brand, each
   showing ITS runtime for the reader's load once there is one, and (3) the
   answer for the chosen card with a meter of load against output.
   COLOUR GRAMMAR: controls are trust blue (focus, the chosen card, a filled
   field); the step numbers are the brand red of the tool's bar; the answer is
   ink/white; "over its output" is amber, never red. */
.mj-rtc__title, .mj-rtc__stamp, .mj-rtc__s1, .mj-rtc__s2{ font:inherit }
.mj-rtc__stamp{ opacity:.75; font-size:.85em }
/* the bubble: says what this is before the instructions do */
.mj-rtc__bubble{ position:relative; display:flex; flex-wrap:wrap; align-items:center; gap:4px 10px;
  width:fit-content; max-width:100%; box-sizing:border-box; margin:0 0 var(--sp-5); padding:9px 18px 9px 14px;
  border-radius:999px; background:var(--mj-ink-900); color:#fff; font-size:.88rem; line-height:1.45;
  box-shadow:0 10px 22px -14px rgba(0,0,0,.6) }
.mj-rtc__pulse{ flex:none; width:9px; height:9px; border-radius:50%; background:var(--mj-red);
  animation:mj-rtc-pulse 1.8s ease-out infinite }
@keyframes mj-rtc-pulse{ 0%{ box-shadow:0 0 0 0 rgba(229,32,38,.65) } 70%{ box-shadow:0 0 0 9px rgba(229,32,38,0) }
  100%{ box-shadow:0 0 0 0 rgba(229,32,38,0) } }
@media (prefers-reduced-motion:reduce){ .mj-rtc__pulse{ animation:none } }
.mj-rtc__bk{ font-family:var(--font-meta); font-size:.64rem; letter-spacing:1.2px; text-transform:uppercase }
.mj-rtc__bt{ color:rgba(255,255,255,.9) }
body.dark-mode .mj-rtc__bubble{ background:#fff; color:#0c0c0e }
body.dark-mode .mj-rtc__bt{ color:#2a2a2e }
/* the three steps */
.mj-rtc__step{ display:flex; align-items:center; gap:10px; margin:0 0 6px; font-family:var(--font-display);
  font-weight:800; font-size:1.06rem; line-height:1.25; color:var(--mj-black) }
.mj-rtc__step--two{ margin-top:var(--sp-6) }
body.dark-mode .mj-rtc__step{ color:#fff }
.mj-rtc__sn{ flex:none; width:26px; height:26px; border-radius:50%; display:grid; place-items:center;
  background:var(--mj-red); color:#fff; font-size:.78rem; font-weight:800 }
.mj-rtc__how{ margin:0 0 var(--sp-3); font-size:.92rem; line-height:1.55; color:var(--mj-text-muted) }
body.dark-mode .mj-rtc__how{ color:#b6b6bb }
/* step 1 · the watt tiles */
.mj-rtc__loads{ list-style:none; margin:0; padding:0; display:grid;
  grid-template-columns:repeat(3, minmax(0,1fr)); gap:10px }
.mj-rtc__load{ display:flex; flex-direction:column; gap:2px; padding:12px 14px 14px; border:1px solid var(--mj-rule);
  border-radius:var(--radius-md); background:#fff; transition:border-color .15s ease, box-shadow .15s ease }
.mj-rtc__load:focus-within{ border-color:var(--mj-trust); box-shadow:0 0 0 3px color-mix(in srgb, var(--mj-trust) 16%, transparent) }
.mj-rtc__load:has(.mj-rtc__watts:not(:placeholder-shown)){ border-color:color-mix(in srgb, var(--mj-trust) 55%, var(--mj-rule)) }
body.dark-mode .mj-rtc__load{ background:#18181b; border-color:#2a2a2d }
body.dark-mode .mj-rtc__load:has(.mj-rtc__watts:not(:placeholder-shown)){ border-color:color-mix(in srgb, var(--mj-trust) 60%, #2a2a2d) }
.mj-rtc__lname{ font-family:var(--font-display); font-weight:700; font-size:.95rem; color:var(--mj-black) }
body.dark-mode .mj-rtc__lname{ color:#fff }
.mj-rtc__lhint{ font-family:var(--font-meta); font-size:.64rem; line-height:1.45; letter-spacing:.2px; color:var(--mj-gray-400) }
.mj-rtc__field{ position:relative; display:block; margin-top:auto; padding-top:10px }
.mj-rtc__watts{ display:block; width:100%; box-sizing:border-box; height:48px; padding:0 40px 0 14px; font:inherit;
  font-family:var(--font-display); font-weight:800; font-size:1.2rem; text-align:left;
  border:1.5px solid color-mix(in srgb, var(--mj-trust) 32%, var(--mj-rule)); border-radius:var(--radius-sm);
  background:color-mix(in srgb, var(--mj-trust) 3%, #fff); color:var(--mj-black); cursor:text;
  -moz-appearance:textfield; appearance:textfield; transition:border-color .15s ease, background-color .15s ease }
.mj-rtc__watts::-webkit-outer-spin-button, .mj-rtc__watts::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0 }
.mj-rtc__watts::placeholder{ font-weight:500; color:#a3a3ab; opacity:1 }
.mj-rtc__watts:hover{ border-color:var(--mj-trust) }
.mj-rtc__watts:focus{ outline:none; border-color:var(--mj-trust); background:#fff }
.mj-rtc__watts:not(:placeholder-shown){ border-color:var(--mj-trust); background:#fff }
body.dark-mode .mj-rtc__watts{ background:#0c0c0e; border-color:color-mix(in srgb, var(--mj-trust) 45%, #2a2a2d); color:#fff }
body.dark-mode .mj-rtc__watts::placeholder{ color:#6b6b73 }
body.dark-mode .mj-rtc__watts:hover, body.dark-mode .mj-rtc__watts:focus,
body.dark-mode .mj-rtc__watts:not(:placeholder-shown){ border-color:#5b8cff; background:#0c0c0e }
.mj-rtc__unit{ position:absolute; right:14px; bottom:13px; font-family:var(--font-display); font-weight:800;
  font-size:1rem; color:var(--mj-gray-400); pointer-events:none }
.mj-rtc__watts:not(:placeholder-shown) + .mj-rtc__unit{ color:var(--mj-black) }
body.dark-mode .mj-rtc__watts:not(:placeholder-shown) + .mj-rtc__unit{ color:#fff }
.mj-rtc__tally{ display:flex; align-items:center; gap:12px; margin:12px 0 0; padding:10px 14px;
  border-radius:var(--radius-md); background:color-mix(in srgb, var(--mj-trust) 6%, var(--mj-surface)) }
body.dark-mode .mj-rtc__tally{ background:#18181b }
.mj-rtc__tk{ font-family:var(--font-meta); font-size:.62rem; font-weight:700; letter-spacing:1px; text-transform:uppercase; color:var(--mj-gray-400) }
.mj-rtc__tv{ font-family:var(--font-display); font-weight:800; font-size:1.2rem; color:var(--mj-black) }
body.dark-mode .mj-rtc__tv{ color:#fff }
.mj-rtc__clear{ margin-left:auto; padding:6px 14px; border:1px solid var(--mj-rule); border-radius:999px; background:transparent;
  font-family:var(--font-meta); font-size:.62rem; font-weight:700; letter-spacing:1px; text-transform:uppercase;
  color:var(--mj-ink); cursor:pointer }
.mj-rtc__clear:hover{ border-color:var(--mj-ink) }
.mj-rtc__clear:disabled{ opacity:.4; cursor:default; border-color:var(--mj-rule) }
body.dark-mode .mj-rtc__clear{ border-color:#3a3a40; color:#e7e7ea }
/* step 2 · brand filters, then the cards */
.mj-rtc__brands{ display:flex; flex-wrap:wrap; gap:8px; margin:6px 0 14px }
.mj-rtc__all, .mj-rtc__brand{ display:inline-flex; align-items:center; gap:8px; padding:8px 12px 8px 16px; border:1px solid var(--mj-rule);
  border-radius:999px; background:#fff; color:var(--mj-black); font:inherit; font-family:var(--font-display); font-weight:700;
  font-size:.88rem; line-height:1; cursor:pointer; transition:border-color .15s ease, background-color .15s ease }
.mj-rtc__all:hover, .mj-rtc__brand:hover{ border-color:var(--mj-ink) }
.mj-rtc__all:focus-visible, .mj-rtc__brand:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
.mj-rtc__all[aria-pressed="true"], .mj-rtc__brand[aria-pressed="true"]{ background:var(--mj-ink-900); border-color:var(--mj-ink-900); color:#fff }
body.dark-mode .mj-rtc__all, body.dark-mode .mj-rtc__brand{ background:#18181b; border-color:#3a3a40; color:#fff }
body.dark-mode .mj-rtc__all[aria-pressed="true"], body.dark-mode .mj-rtc__brand[aria-pressed="true"]{ background:#fff; border-color:#fff; color:#0c0c0e }
.mj-rtc__bl{ font:inherit }
.mj-rtc__bn, .mj-rtc__an{ padding:3px 7px; border-radius:999px; background:rgba(0,0,0,.07); font-family:var(--font-meta); font-size:.62rem; font-weight:700 }
[aria-pressed="true"] > .mj-rtc__bn, [aria-pressed="true"] > .mj-rtc__an{ background:rgba(255,255,255,.2) }
body.dark-mode .mj-rtc__bn, body.dark-mode .mj-rtc__an{ background:rgba(255,255,255,.12) }
body.dark-mode [aria-pressed="true"] > .mj-rtc__bn, body.dark-mode [aria-pressed="true"] > .mj-rtc__an{ background:rgba(0,0,0,.08) }
/* J-5b (Lucky, 2026-10-04): "the design is a bit too long … they don't see the
   recommendation unless they scroll". The cards are ONE swipeable row (a peek of
   the next card says there is more; arrows for a mouse), and a slim live bar
   sticks to the bottom of the screen while the reader is inside the tool,
   carrying the runtime as they type. It hides once the full answer is on screen.
   The tool frame clips instead of hiding overflow, so the bar can stick. */
.mj-rtc{ overflow:clip }
.mj-rtc__pickhead{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:var(--sp-6) }
.mj-rtc__pickhead .mj-rtc__step--two{ margin:0 }
.mj-rtc__nav{ display:flex; gap:6px; flex:none }
.mj-rtc__prev, .mj-rtc__next{ width:36px; height:36px; border-radius:50%; border:1px solid var(--mj-rule); background:#fff;
  cursor:pointer; position:relative; transition:border-color .15s ease, opacity .15s ease }
.mj-rtc__prev::before, .mj-rtc__next::before{ content:""; position:absolute; left:50%; top:50%; width:8px; height:8px;
  border-top:2px solid var(--mj-black); border-right:2px solid var(--mj-black) }
.mj-rtc__next::before{ transform:translate(-65%,-50%) rotate(45deg) }
.mj-rtc__prev::before{ transform:translate(-35%,-50%) rotate(-135deg) }
.mj-rtc__prev:hover:not(:disabled), .mj-rtc__next:hover:not(:disabled){ border-color:var(--mj-ink) }
.mj-rtc__prev:disabled, .mj-rtc__next:disabled{ opacity:.35; cursor:default }
.mj-rtc__prev:focus-visible, .mj-rtc__next:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
body.dark-mode .mj-rtc__prev, body.dark-mode .mj-rtc__next{ background:#18181b; border-color:#3a3a40 }
body.dark-mode .mj-rtc__prev::before, body.dark-mode .mj-rtc__next::before{ border-color:#fff }
.mj-rtc__units{ display:grid; grid-auto-flow:column; grid-auto-columns:calc((100% - 20px) / 3.15); gap:10px;
  overflow-x:auto; overscroll-behavior-x:contain; scroll-snap-type:x mandatory; scroll-padding:0 2px;
  padding:3px 2px 10px; scrollbar-width:thin }
.mj-rtc__units > .mj-rtc__card{ scroll-snap-align:start }
/* the live answer */
.mj-rtc__live{ position:sticky; bottom:12px; z-index:5; display:flex; align-items:baseline; flex-wrap:wrap; gap:4px 10px;
  margin:var(--sp-4) 0 0; padding:11px 12px 11px 18px; border-radius:999px; background:var(--mj-ink-900); color:#fff;
  box-shadow:0 12px 28px -12px rgba(0,0,0,.55) }
.mj-rtc__live[hidden]{ display:none }
.mj-rtc__lvk{ font-family:var(--font-meta); font-size:.6rem; letter-spacing:1px; text-transform:uppercase; opacity:.7; white-space:nowrap }
.mj-rtc__lvh{ font-family:var(--font-display); font-weight:800; font-size:1.15rem }
.mj-rtc[data-over="1"] .mj-rtc__lvh{ color:#f3c56b }
.mj-rtc__lvu{ font-size:.82rem; opacity:.8; min-width:0 }
.mj-rtc__lvgo{ margin-left:auto; align-self:center; padding:7px 14px; border:0; border-radius:999px; background:#fff; color:#0c0c0e;
  font-family:var(--font-meta); font-size:.6rem; font-weight:700; letter-spacing:1px; text-transform:uppercase; cursor:pointer }
.mj-rtc__lvgo::after{ content:" \2193" }
body.dark-mode .mj-rtc__live{ background:#fff; color:#0c0c0e }
body.dark-mode .mj-rtc__lvgo{ background:#0c0c0e; color:#fff }
body.dark-mode .mj-rtc[data-over="1"] .mj-rtc__lvh{ color:#8a5a00 }
.mj-rtc__card{ position:relative; display:grid; grid-template-columns:72px minmax(0,1fr); align-items:start; align-content:start; gap:8px 12px;
  padding:12px; border:1.5px solid var(--mj-rule); border-radius:var(--radius-md); background:#fff; cursor:pointer;
  transition:border-color .15s ease, box-shadow .15s ease, transform .15s ease }
.mj-rtc__card[hidden]{ display:none }
.mj-rtc__card:hover{ border-color:color-mix(in srgb, var(--mj-trust) 50%, var(--mj-rule)); transform:translateY(-1px);
  box-shadow:0 8px 18px -12px rgba(0,0,0,.4) }
.mj-rtc__card:has(.mj-rtc__radio:checked){ border-color:var(--mj-trust); box-shadow:0 0 0 3px color-mix(in srgb, var(--mj-trust) 16%, transparent) }
.mj-rtc__card:has(.mj-rtc__radio:focus-visible){ outline:2px solid var(--mj-trust); outline-offset:2px }
body.dark-mode .mj-rtc__card{ background:#18181b; border-color:#2a2a2d }
body.dark-mode .mj-rtc__card:has(.mj-rtc__radio:checked){ border-color:#5b8cff }
.mj-rtc__card::after{ content:""; position:absolute; top:10px; right:10px; width:18px; height:18px; box-sizing:border-box;
  border-radius:50%; border:1.5px solid var(--mj-rule); background:#fff }
.mj-rtc__card:has(.mj-rtc__radio:checked)::after{ border-color:var(--mj-trust);
  background:var(--mj-trust) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center/11px no-repeat }
body.dark-mode .mj-rtc__card::after{ background-color:#0c0c0e; border-color:#3a3a40 }
body.dark-mode .mj-rtc__card:has(.mj-rtc__radio:checked)::after{ background-color:#5b8cff; border-color:#5b8cff }
.mj-rtc__radio{ position:absolute; top:0; left:0; width:1px; height:1px; margin:0; opacity:0; pointer-events:none }
.mj-rtc__shot{ grid-row:1 / span 2; width:72px; height:72px; box-sizing:border-box; padding:5px; border-radius:var(--radius-sm);
  background:#fff; border:1px solid var(--mj-rule); display:grid; place-items:center; overflow:hidden }
body.dark-mode .mj-rtc__shot{ border-color:#2a2a2d }
.mj-rtc__img{ width:100%; height:100%; object-fit:contain }
.mj-rtc__cb{ display:grid; gap:3px; min-width:0; padding-right:22px }
.mj-rtc__cn{ font-family:var(--font-display); font-weight:800; font-size:.92rem; line-height:1.2; color:var(--mj-black) }
body.dark-mode .mj-rtc__cn{ color:#fff }
.mj-rtc__cs, .mj-rtc__cg{ font-family:var(--font-meta); font-size:.64rem; line-height:1.45; letter-spacing:.2px; color:var(--mj-text-muted) }
.mj-rtc__cg{ margin-top:-3px; color:var(--mj-gray-400) }
body.dark-mode .mj-rtc__cs{ color:#a8a8ae }
.mj-rtc__cp{ display:flex; align-items:baseline; flex-wrap:wrap; gap:0 5px }
.mj-rtc__cpv{ font-family:var(--font-display); font-weight:800; font-size:.95rem; color:var(--mj-black) }
body.dark-mode .mj-rtc__cpv{ color:#fff }
.mj-rtc__cps{ font-family:var(--font-meta); font-size:.6rem; color:var(--mj-gray-400) }
.mj-rtc__each{ grid-column:2; justify-self:start; padding:3px 9px; border-radius:999px; font-family:var(--font-display);
  font-weight:700; font-size:.78rem; background:color-mix(in srgb, var(--mj-growth) 13%, #fff); color:var(--mj-growth-text) }
.mj-rtc__each:empty{ display:none }
.mj-rtc__card[data-state="over"] .mj-rtc__each{ background:#fdf1dc; color:#8a5a00 }
body.dark-mode .mj-rtc__each{ background:color-mix(in srgb, var(--mj-growth) 22%, #18181b); color:#7fe0ad }
body.dark-mode .mj-rtc__card[data-state="over"] .mj-rtc__each{ background:#3a2a0c; color:#f3c56b }
.mj-rtc__basis{ margin:var(--sp-4) 0 0; font-size:.8rem; line-height:1.5; color:var(--mj-gray-400) }
/* step 3 · the answer */
.mj-rtc__out{ display:grid; gap:6px; min-width:0 }
.mj-rtc__on{ font-family:var(--font-meta); font-size:.64rem; letter-spacing:1px; text-transform:uppercase }
.mj-rtc .mj-cta__result .mj-rtc__on{ color:rgba(255,255,255,.65) }
.mj-rtc__uname{ color:#fff }
.mj-rtc__runtime{ margin:0; font-family:var(--font-display); font-size:1rem }
.mj-rtc__hours{ font-weight:800; font-size:1.6rem; line-height:1.1 }
.mj-rtc__meter{ height:6px; border-radius:3px; background:rgba(255,255,255,.16); overflow:hidden; margin:4px 0 2px; max-width:420px }
.mj-rtc__bar{ display:block; height:100%; width:0; border-radius:3px; background:#fff; transition:width .25s ease }
.mj-rtc[data-over="1"] .mj-rtc__bar{ background:#f3c56b }
.mj-rtc__fit, .mj-rtc__surge{ margin:0; font-size:.9rem; line-height:1.5 }
.mj-rtc__surge{ padding:8px 10px; border-radius:var(--radius-sm); background:rgba(255,255,255,.12) }
.mj-rtc[data-over="1"] .mj-rtc__fit{ font-weight:700 }
.mj-rtc[data-over="1"] .mj-rtc__hours{ color:#f3c56b }
.mj-rtc__rlead{ font-weight:600; opacity:.85 }
.mj-rtc__cta{ white-space:normal; text-align:center }
@media(max-width:820px){ .mj-rtc__loads{ grid-template-columns:repeat(2, minmax(0,1fr)) }
  .mj-rtc__units{ grid-auto-columns:calc((100% - 10px) / 2.1) } }
@media(max-width:560px){
  .mj-rtc__loads{ grid-template-columns:repeat(2, minmax(0,1fr)); gap:8px }
  .mj-rtc__load{ padding:10px 11px 12px }
  .mj-rtc__watts{ height:44px; font-size:1.08rem; padding:0 32px 0 11px }
  .mj-rtc__unit{ right:11px; bottom:12px }
  .mj-rtc__units{ grid-auto-columns:82%; gap:8px }
  .mj-rtc__nav{ display:none }
  .mj-rtc__live{ bottom:8px; flex-wrap:nowrap; align-items:center; gap:8px; padding:7px 7px 7px 14px }
  .mj-rtc__lvh{ font-size:1.02rem; white-space:nowrap }
  .mj-rtc__lvgo{ padding:7px 11px; white-space:nowrap }
  .mj-rtc__lvu{ display:none }
  .mj-rtc__card{ grid-template-columns:60px minmax(0,1fr); padding:10px }
  .mj-rtc__shot{ width:60px; height:60px }
  .mj-rtc__bubble{ font-size:.84rem }
}

/* =============================================================================
   THE NEWSROOM SECTIONS  (.mj-nx-*)  —  2026-09-23, Phase 4
   Prototype: mashjoy-sections--newsroom.html

   Lucky's brief: build the sections he chose from the Newsreader demos — City
   "Transport", Gear "Innovations" and "Gadgets", News "Politics" and "Most
   discussed", the City hero and the News hero — IN FULL, in the prototype, with
   pictures, so the homepage, Just In and every archive can reuse them.

   ONE SET OF CARDS, SEVEN ARRANGEMENTS. Every section here is built from the same
   six atoms, so the site still reads as one publication however the sections
   are mixed:
     .mj-nx-card   picture on top, words under          (lead / default / sm)
     .mj-nx-row    words beside a picture               (--thumb: small square
                                                         left · --right: wide right)
     .mj-nx-tile   words ON the picture                 (--tall: portrait)
     .mj-nx-text   words only
     .mj-nx-num    a big numeral, words, optional thumb
     .mj-nx-head   the section's name and "Discover all"

   OURS, NOT THEIRS. Newsreader sets headlines in a serif; this is Technor, the
   brand's display face, so the site keeps its own voice. Kickers are WORDS ONLY
   — Lucky, 2026-09-23: "remove all these red dots everywhere … just keep it as
   words only". Columns are ruled with the thin grey rule, never a black bar.
   ============================================================================= */
.mj-nx{ margin:0 0 var(--sp-7); padding:0 0 var(--sp-6); border-bottom:1px solid var(--mj-rule) }
.mj-nx *{ min-width:0 }
.mj-nx a{ color:inherit; text-decoration:none }

/* ── the section head: its name, then "Discover all ›" ── */
.mj-nx-head{ display:flex; flex-wrap:wrap; align-items:baseline; gap:4px var(--sp-3); margin:0 0 var(--sp-4) }
.mj-nx-head__t{ margin:0; font-family:var(--font-display); font-weight:800; font-size:1.05rem;
  letter-spacing:-.01em; color:var(--mj-black) }
.mj-nx-head__all{ font-family:var(--font-meta); font-size:.74rem; color:var(--mj-text-muted); white-space:nowrap;
  text-decoration:underline !important; text-underline-offset:3px }
.mj-nx-head__all::after{ content:" ›" }
.mj-nx-head__all:hover{ color:var(--mj-red) }
/* A desk that spans verticals names them beside its title (2026-09-23). */
.mj-nx-head__links{ display:flex; flex-wrap:wrap; gap:4px 16px }
.mj-nx-head__l{ font-family:var(--font-meta); font-size:.74rem; color:var(--mj-text-muted);
  text-decoration:none; white-space:nowrap }
.mj-nx-head__l:hover{ color:var(--mj-red) }

/* THE EMPTY PICTURE (2026-09-23). Sits UNDER every picture: shows the desk's name on a
   quiet panel until the Studio gives the article a thumbnail, and is what a reader sees
   if a picture fails — never a dark "image pending" box or a broken-image glyph. */
.mj-nx-pic__ph{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  padding:8px; text-align:center; font-family:var(--font-meta); font-weight:700; font-size:.6rem;
  line-height:1.3; letter-spacing:1.2px; text-transform:uppercase; color:var(--mj-text-muted);
  background:var(--mj-surface-2, #eef0f3)
    repeating-linear-gradient(135deg, rgba(0,0,0,.028) 0 1px, transparent 1px 9px) }
.mj-nx-pic img{ position:relative; z-index:1 }
.mj-nx-row .mj-nx-pic__ph{ font-size:.5rem; letter-spacing:.8px; padding:4px }
/* a tile's own kicker already names the desk, so its panel carries no label */
.mj-nx-tile > .mj-nx-pic__ph{ font-size:0; background:#1f1f24
    repeating-linear-gradient(135deg, rgba(255,255,255,.035) 0 1px, transparent 1px 10px) }
.mj-nx-tile img{ z-index:1 }
.mj-nx-tile::after{ z-index:1 }
/* The atoms exhibit (prototype only; the site never emits it). */
.mj-nx-atoms{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--sp-6); align-items:start }

/* ── the words every atom shares ── */
.mj-nx-k{ display:block; margin:0 0 6px; font-family:var(--font-meta); font-weight:700;
  font-size:.6rem; letter-spacing:1.3px; text-transform:uppercase; color:var(--mj-red) }
.mj-nx-by{ display:block; margin-top:6px; font-family:var(--font-meta); font-size:.72rem;
  line-height:1.4; color:var(--mj-text-muted) }
.mj-nx-by b{ font-weight:600; color:var(--mj-ink) }
.mj-nx-by time::before{ content:" · " }
.mj-nx-d{ margin:10px 0 0; font-family:var(--font-body); font-size:1rem; line-height:1.55;
  color:var(--mj-text-muted) }
.mj-nx-t{ margin:0; font-family:var(--font-display); font-weight:700; font-size:1rem;
  line-height:1.22; letter-spacing:-.005em; color:var(--mj-black); transition:color .15s }
a:hover > .mj-nx-t, a:hover .mj-nx-t, .mj-nx-t a:hover{ color:var(--mj-trust) }

/* ── a picture: always cropped, never stretched, lightly zoomed on hover ── */
.mj-nx-pic{ display:block; position:relative; overflow:hidden; background:var(--mj-surface-2, #eef0f3);
  border-radius:2px; aspect-ratio:16/9 }
.mj-nx-pic img{ display:block; width:100%; height:100%; object-fit:cover;
  transition:transform .5s cubic-bezier(.2,.6,.2,1) }
a:hover .mj-nx-pic img, .mj-nx-pic:hover img{ transform:scale(1.035) }
.mj-nx-pic--sq{ aspect-ratio:1/1 }
.mj-nx-pic--43{ aspect-ratio:4/3 }
.mj-nx-pic--32{ aspect-ratio:3/2 }

/* ── CARD: picture on top ── */
.mj-nx-card{ display:block }
.mj-nx-card .mj-nx-pic{ margin-bottom:12px }
.mj-nx-card--lead .mj-nx-t{ font-size:clamp(1.45rem, 2.1vw, 1.95rem); line-height:1.12;
  letter-spacing:-.015em; font-weight:800 }
.mj-nx-card--lead .mj-nx-pic{ margin-bottom:16px }
.mj-nx-card--md .mj-nx-t{ font-size:1.15rem }
.mj-nx-card--sm .mj-nx-t{ font-size:.95rem }

/* ── ROW: words beside a picture ── */
.mj-nx-row{ display:grid; gap:14px; align-items:start; padding:14px 0;
  border-top:1px solid var(--mj-rule) }
/* THE BASE ROW IS THE THUMBED ROW (--thumb is kept as its name in markup), so the
   one row the site's template is cut from is already the common case. */
.mj-nx-row, .mj-nx-row--thumb{ grid-template-columns:76px minmax(0,1fr) }
.mj-nx-row .mj-nx-pic{ aspect-ratio:1/1 }
.mj-nx-row .mj-nx-t{ font-size:.9rem }
/* The picture is a companion to the words, never the bigger half: capped at 280px so
   a row spanning a laptop-width column does not turn into a second lead. */
.mj-nx-row--right{ grid-template-columns:minmax(0,1fr) min(38%, 280px) }
.mj-nx-row--right .mj-nx-pic{ grid-column:2; grid-row:1; aspect-ratio:3/2 }
.mj-nx-row--right > .mj-nx-row__b{ grid-column:1; grid-row:1 }
.mj-nx-row--right .mj-nx-t{ font-size:1.15rem }
.mj-nx-row__b{ display:block }

/* ── TILE: words on the picture ── */
.mj-nx-tile{ position:relative; display:block; overflow:hidden; border-radius:2px;
  aspect-ratio:16/10; background:#18181b; color:#fff }
.mj-nx-tile img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  transition:transform .5s cubic-bezier(.2,.6,.2,1) }
.mj-nx-tile:hover img{ transform:scale(1.035) }
.mj-nx-tile::after{ content:""; position:absolute; inset:0;
  background:linear-gradient(to top, rgba(0,0,0,.84) 0%, rgba(0,0,0,.42) 42%, rgba(0,0,0,0) 72%) }
.mj-nx-tile__b{ position:absolute; left:0; right:0; bottom:0; z-index:2; padding:18px 20px }
.mj-nx-tile .mj-nx-k{ color:#fff; opacity:.92 }
.mj-nx-tile .mj-nx-t{ color:#fff; font-size:1.2rem; line-height:1.18 }
.mj-nx-tile .mj-nx-by, .mj-nx-tile .mj-nx-by b{ color:rgba(255,255,255,.82) }
.mj-nx-tile:hover .mj-nx-t{ color:#fff; text-decoration:underline; text-underline-offset:4px;
  text-decoration-thickness:2px }
.mj-nx-tile--tall{ aspect-ratio:3/4 }
.mj-nx-tile--big{ aspect-ratio:16/9 }
.mj-nx-tile--big .mj-nx-t{ font-size:clamp(1.5rem, 2.4vw, 2.2rem); line-height:1.1; font-weight:800 }
.mj-nx-tile--big .mj-nx-tile__b{ padding:26px 30px }

/* ── TEXT: words only ── */
.mj-nx-text{ display:block; padding:14px 0; border-top:1px solid var(--mj-rule) }
.mj-nx-text .mj-nx-t{ font-size:.95rem }

/* ── NUMBERED: the numeral carries the order ── */
.mj-nx-num{ display:grid; grid-template-columns:44px minmax(0,1fr); gap:10px; align-items:start;
  padding:14px 0; border-bottom:1px solid var(--mj-rule) }
.mj-nx-num__n{ font-family:var(--font-display); font-weight:800; font-size:2.1rem; line-height:.95;
  color:var(--mj-red); text-align:center }
.mj-nx-num--thumb{ grid-template-columns:28px 64px minmax(0,1fr) }
.mj-nx-num--thumb .mj-nx-num__n{ font-size:1.5rem }
.mj-nx-num--thumb .mj-nx-t{ font-size:.9rem }

/* ── the ruled columns every multi-column section uses ── */
.mj-nx-col{ display:block }
.mj-nx-col--ruled{ padding-left:var(--sp-5); border-left:1px solid var(--mj-rule) }
.mj-nx-col > :first-child{ padding-top:0; border-top:0 }
.mj-nx-col > .mj-nx-card + .mj-nx-row,
.mj-nx-col > .mj-nx-tile + .mj-nx-text,
.mj-nx-col > .mj-nx-tile + .mj-nx-row{ margin-top:14px }

/* =============================================================================
   1 · FRONT  (.mj-nx-front) — City demo's "Transport" section, the HOMEPAGE MAIN.
   Three ruled columns: a picture card and four thumbed rows either side of one
   dominant story, with a picture-right row beneath it. Then TOP (.mj-nx-top):
   ten numbered stories in two columns — which is "Latest across the desk".
   ============================================================================= */
.mj-nx-front__g{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,2.15fr) minmax(0,1fr);
  gap:var(--sp-5) }
.mj-nx-top{ margin-top:var(--sp-6) }
.mj-nx-top__g{ display:grid; grid-template-columns:1fr 1fr; column-gap:var(--sp-7);
  border-top:1px solid var(--mj-rule) }

/* 2 · INNOVATIONS (.mj-nx-innov) — Gear demo. A small tile over five text rows,
   the lead with two picture-right rows, and a tall portrait story where the demo
   runs an advertisement (we do not run display ads). */
.mj-nx-innov__g{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,2.15fr) minmax(0,1fr);
  gap:var(--sp-5) }

/* 3 · GADGETS (.mj-nx-gadgets) — Gear demo. Three tiles, then two rows of three
   thumbed rows. */
.mj-nx-gadgets__tiles{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--sp-4) }
.mj-nx-gadgets__rows{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); column-gap:var(--sp-5);
  margin-top:var(--sp-4) }

/* 4 · CITY HERO (.mj-nx-city) — the lead set beside its picture, three cards
   under it, and "Latest" down the right rail. The Just In main section. */
.mj-nx-city__g{ display:grid; grid-template-columns:minmax(0,3fr) minmax(0,1fr); gap:var(--sp-6) }
.mj-nx-city__lead{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.55fr); gap:var(--sp-5);
  align-items:start; padding-bottom:var(--sp-5); border-bottom:1px solid var(--mj-rule) }
.mj-nx-city__lead .mj-nx-pic{ grid-column:2; grid-row:1; aspect-ratio:16/10 }
.mj-nx-city__lead .mj-nx-card__b{ grid-column:1; grid-row:1 }
.mj-nx-city__lead .mj-nx-t{ font-size:clamp(1.6rem, 2.4vw, 2.2rem); line-height:1.1; font-weight:800 }
.mj-nx-city__three{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--sp-5);
  margin-top:var(--sp-5) }
.mj-nx-rail__h{ margin:0 0 var(--sp-3); font-family:var(--font-meta); font-weight:700; font-size:.66rem;
  letter-spacing:1.3px; text-transform:uppercase; color:var(--mj-black) }

/* 5 · POLITICS (.mj-nx-pol) — News demo. The lead on the left; two picture-right
   rows and a two-by-two of text on the right. */
.mj-nx-pol__g{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:var(--sp-6) }
.mj-nx-pol__four{ display:grid; grid-template-columns:1fr 1fr; column-gap:var(--sp-5) }

/* 6 · MOST DISCUSSED (.mj-nx-disc) — News demo. Four cards over four text items. */
.mj-nx-disc__cards, .mj-nx-disc__texts{ display:grid; grid-template-columns:repeat(4,minmax(0,1fr));
  column-gap:var(--sp-5) }
.mj-nx-disc__texts{ margin-top:var(--sp-4) }

/* 7 · NEWS HERO (.mj-nx-news) — News demo. A card and five thumbed rows; a big
   tile, two tiles, two picture-right rows; and a tabbed, numbered rail. For an
   archive's full set, in place of "Everything we have published here". */
.mj-nx-news__g{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,2.15fr) minmax(0,1fr);
  gap:var(--sp-5) }
.mj-nx-news__pair{ display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-4); margin-top:var(--sp-4) }
.mj-nx-tabs{ display:flex; gap:var(--sp-4); margin:0 0 var(--sp-2); border-bottom:1px solid var(--mj-rule) }
.mj-nx-tabs__b{ appearance:none; background:none; border:0; padding:0 0 8px; margin-bottom:-1px;
  cursor:pointer; font-family:var(--font-meta); font-weight:700; font-size:.66rem; letter-spacing:1.1px;
  text-transform:uppercase; color:var(--mj-text-muted); border-bottom:2px solid transparent }
.mj-nx-tabs__b[aria-selected="true"]{ color:var(--mj-black); border-bottom-color:var(--mj-red) }
.mj-nx-tabs__b:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
.mj-nx-tabs__p[hidden]{ display:none }

/* ── DARK ── */
body.dark-mode .mj-nx-pic{ background:#1c1c20 }
body.dark-mode .mj-nx-pic__ph{ background-color:#1c1c20; color:#8a8a92 }
body.dark-mode .mj-nx-by b{ color:#e7e7ea }

/* ── RESPONSIVE: the same sections at every width, never squeezed ──
   ≥1180 as designed · 900–1179 three columns become lead-over-two ·
   600–899 two columns · <600 one column, rows keep their picture beside them. */
@media(max-width:1179px){
  .mj-nx-front__g, .mj-nx-innov__g, .mj-nx-news__g{ grid-template-columns:1fr 1fr }
  .mj-nx-front__g > .mj-nx-col--main, .mj-nx-innov__g > .mj-nx-col--main,
  .mj-nx-news__g > .mj-nx-col--main{ grid-column:1 / -1; grid-row:1; padding-left:0; border-left:0 }
  .mj-nx-front__g > .mj-nx-col:first-child, .mj-nx-innov__g > .mj-nx-col:first-child,
  .mj-nx-news__g > .mj-nx-col:first-child{ padding-left:0; border-left:0 }
  .mj-nx-city__g{ grid-template-columns:1fr }
  .mj-nx-city__g > .mj-nx-col--ruled{ padding-left:0; border-left:0; padding-top:var(--sp-5);
    border-top:1px solid var(--mj-rule) }
  /* Three column wrappers of two rows wrap 2+1 in two columns (a 4/2 split at
     1017). Below the desktop the rows join the grid directly and flow 3+3. */
  .mj-nx-gadgets__rows{ grid-template-columns:1fr 1fr }
  .mj-nx-gadgets__rows > .mj-nx-col{ display:contents }
  .mj-nx-gadgets__rows .mj-nx-row{ padding-top:14px; border-top:1px solid var(--mj-rule) }
}
/* THE LEAD SITS BESIDE ITS PICTURE BETWEEN 600 AND 1179 (2026-09-23). Spanning a
   laptop-width row, a 16:9 lead picture stood ~560px tall over a modest headline —
   the picture shouting, the story whispering. Side by side, the way a newspaper
   front sets its lead, the two carry equal weight and the section is half as tall. */
@media(min-width:600px) and (max-width:1179px){
  .mj-nx-col--main > .mj-nx-card--lead{ display:grid; grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);
    gap:var(--sp-5); align-items:center; padding-bottom:var(--sp-4) }
  .mj-nx-col--main > .mj-nx-card--lead .mj-nx-pic{ margin-bottom:0 }
  .mj-nx-col--main > .mj-nx-card--lead .mj-nx-t{ font-size:clamp(1.45rem, 2.9vw, 1.9rem) }
  /* The "Latest" rail, once it drops under the lead, spans the row: its 16:9
     picture stood ~580px tall at 1017. Card on the left, three rows beside it. */
  .mj-nx-city__g > .mj-nx-col--ruled{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    column-gap:var(--sp-5); align-content:start }
  .mj-nx-city__g > .mj-nx-col--ruled > .mj-nx-rail__h{ grid-column:1 / -1 }
  .mj-nx-city__g > .mj-nx-col--ruled > .mj-nx-card--md{ grid-column:1; grid-row:2 / span 3 }
  .mj-nx-city__g > .mj-nx-col--ruled > .mj-nx-row{ grid-column:2 }
  .mj-nx-city__g > .mj-nx-col--ruled > .mj-nx-card--md + .mj-nx-row{ margin-top:0; padding-top:0; border-top:0 }
}
/* ONCE POLITICS STACKS (600–899), its lead sits beside its words like every other lead,
   and the spanning Gadgets tile takes a wider crop: at 800 both stood ~460px tall. */
@media(min-width:600px) and (max-width:899px){
  .mj-nx-pol__g .mj-nx-card--lead{ display:grid; grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);
    gap:var(--sp-5); align-items:center }
  .mj-nx-pol__g .mj-nx-card--lead .mj-nx-pic{ margin-bottom:0 }
  .mj-nx-gadgets__tiles > :first-child{ aspect-ratio:21/9 }
}
@media(max-width:899px){
  /* Four cards across hold down to 900px (~210px each at 1017); below that, two. */
  .mj-nx-disc__cards, .mj-nx-disc__texts{ grid-template-columns:1fr 1fr; row-gap:var(--sp-4) }
  .mj-nx-pol__g{ grid-template-columns:1fr }
  .mj-nx-pol__g > .mj-nx-col--ruled{ padding-left:0; border-left:0 }
  .mj-nx-top__g{ grid-template-columns:1fr; column-gap:0 }
  .mj-nx-gadgets__tiles{ grid-template-columns:1fr 1fr }
  .mj-nx-gadgets__tiles > :first-child{ grid-column:1 / -1 }
}
@media(max-width:599px){
  .mj-nx-front__g, .mj-nx-innov__g, .mj-nx-news__g, .mj-nx-gadgets__rows,
  .mj-nx-disc__cards, .mj-nx-disc__texts, .mj-nx-pol__four, .mj-nx-city__three,
  .mj-nx-gadgets__tiles, .mj-nx-news__pair, .mj-nx-atoms{ grid-template-columns:1fr }
  .mj-nx-col--ruled{ padding-left:0; border-left:0 }
  .mj-nx-front__g > .mj-nx-col + .mj-nx-col, .mj-nx-innov__g > .mj-nx-col + .mj-nx-col,
  .mj-nx-news__g > .mj-nx-col + .mj-nx-col{ padding-top:var(--sp-4); border-top:1px solid var(--mj-rule) }
  .mj-nx-row--right{ grid-template-columns:minmax(0,1fr) 34% }
  .mj-nx-row--right .mj-nx-t{ font-size:1rem }
  .mj-nx-tile--tall{ aspect-ratio:4/3 }
  .mj-nx-tile--big .mj-nx-tile__b{ padding:18px 20px }
  /* ON A PHONE, SUPPORTING CARDS BECOME THUMBED ROWS. Stacked full-width, three
     16:9 cards under a lead that already has a full-width picture made the City
     section 2,100px tall at 390. The first Most-discussed card keeps its picture. */
  .mj-nx-city__three > .mj-nx-card, .mj-nx-disc__cards > .mj-nx-card + .mj-nx-card{ display:grid;
    grid-template-columns:96px minmax(0,1fr); gap:14px; align-items:start; padding-top:14px;
    border-top:1px solid var(--mj-rule) }
  .mj-nx-city__three > .mj-nx-card .mj-nx-pic, .mj-nx-disc__cards > .mj-nx-card + .mj-nx-card .mj-nx-pic{
    aspect-ratio:1/1; margin-bottom:0 }
  .mj-nx-city__three, .mj-nx-disc__cards{ row-gap:14px }
  .mj-nx-city__lead{ grid-template-columns:1fr }
  .mj-nx-city__lead .mj-nx-pic, .mj-nx-city__lead .mj-nx-card__b{ grid-column:1; grid-row:auto }
}

/* WORDS ONLY (Lucky, 2026-09-23): "remove all these red dots everywhere because they just
   feel a little bit more AI, and just keep it as words only". Found by asking the rendered
   pages which elements paint a small dot before their text (render_audit --dots). The
   Just In lightning and the short red rules in the menu are icons and rules, not dots. */
.mj-nl-k::before, .mj-finder__k::before, .mj-lead__k::before, .mm-feat__chip::before,
.mj-sum__pill::before, .mj-sum .mj-finder[data-layout="embed"] .mj-finder__k::before{ content:none }

/* THE MEGA MENU MUST STILL FOLD AWAY BELOW 900px (2026-09-23). `body.chrome-mega .mj-nav
   { display:flex }` (the mega-menu block) is later and more specific than the ≤900px rule
   that hides the desktop menu, so every mega-menu page kept its full desktop menu on a
   phone: the header ran to 1,092px at 800 and phones laid the page out 967px wide. Found by
   the homepage's width checks, which measure scrollWidth against the viewport. */
@media(max-width:900px){ body.chrome-mega .mj-nav{ display:none } }

/* THE MENU LABELS STAY ON ONE LINE (2026-09-23). Between 901 and ~1390px the five desk
   labels wrapped ("CARD / MACHINES"), which made the bar look broken on a laptop. One
   line, a step smaller, and the gaps already shrink with the width above. */
@media(min-width:1001px) and (max-width:1399px){
  .mj-nav .mm-top{ white-space:nowrap; font-size:.7rem; letter-spacing:.3px }
  .mj-nav .mm-top .chev{ display:none }
}
/* Below 1000px even one line of five desks does not fit beside the logo and the
   buttons (610px of labels against ~515px at 901), so the drawer takes over — the same
   menu the phone already uses, rather than a bar that wraps or runs off the page. */
@media(max-width:1000px){
  body.chrome-mega .mj-nav{ display:none }
  body.chrome-mega .mj-burger{ display:block }
}

/* ═══════════════════════════════════════════════════════════════════════════
   INTERNATIONAL PRICES · `.mj-fxv`, `.mj-fxswitch`, `.mj-fxnote`, `.mj-deal`
   Lucky, 2026-09-23 (quality pass item 1): "if that company is international and
   the guide is for South Africans, put the price in US dollars as quoted …
   include a way to get the customer to have that price converted to rands as an
   estimate … an added feature they can interact with so they don't have to do
   the conversions themselves." And on 23 September: "make sure that people can
   also easily understand and see the price in rands."

   THE RULES THIS ENCODES (prototype: mashjoy-price-international.html)
     1. A price prints in the currency the vendor charges a South African in.
        Mailchimp, Zoho and Xero quote rand to South Africans: those stay rand.
     2. A dollar price ALWAYS carries its rand estimate beside it, smaller, so
        nobody has to convert. `.mj-fxv` holds both figures, both rendered by
        the build from ONE dated rate (site/content/fx.json).
     3. One switch per page flips which figure leads. The dollar figure never
        disappears, because it is the one on the invoice.
     4. The basis is stated once per element: rate, date, and that the bank may
        add a fee. An estimate that hides its basis is a price claim.
     5. An offer says whose it is. "Only through our link" and "FreshBooks'
        offer today" are different facts; a coupon shows its code; a deadline
        shows a live countdown and the offer hides itself when it passes.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── the dual figure ── */
.mj-fxv{ display:inline-flex; align-items:baseline; gap:.45em; flex-wrap:wrap; line-height:1.1 }
.mj-fxv__usd, .mj-fxv__zar{ font-family:var(--font-display); font-weight:800; font-style:normal }
/* default: the currency on the invoice leads */
/* ONE SIZE, ONE LINE (Lucky, 2026-10-01, 2.1): "make sure … the prices are on the same line … make
   that price to be the same size … the price you mention as the main price … give that a blue
   colour … and then the other part be gray." In an element the leading currency is mashjoy.com
   in body ink and the other grey at the same size; in prose (.mj-fxv--inline) black and grey.
   Lucky, 2026-10-01: NOT mashjoy.com blue. Blue on prices and reading text "cheapens the brand" and
   reads as AI; blue is for controls and small labels only (switch, offer tag, accepts label). */
.mj-fxv__usd{ color:var(--mj-ink) }
.mj-fxv__zar{ font-size:1em; color:var(--mj-gray-400); letter-spacing:.01em; white-space:nowrap }
body.dark-mode .mj-fxv__usd{ color:inherit }
.mj-fxv__zar::before{ content:"\2248\00a0" }                /* ≈ */
/* the switch set to rand: the estimate leads and the billed figure steps back */
html.fx-zar .mj-fxv{ flex-direction:row-reverse; justify-content:flex-end }
html.fx-zar .mj-fxv__zar{ font-size:1em; color:var(--mj-ink) }
body.dark-mode html.fx-zar .mj-fxv__zar, html.fx-zar body.dark-mode .mj-fxv__zar{ color:inherit }
html.fx-zar .mj-fxv__usd{ font-size:1em; color:var(--mj-gray-400) }
html.fx-zar .mj-fxv__usd::after{ content:"\00a0 billed"; font-family:var(--font-meta);
  font-weight:600; font-size:.8em; letter-spacing:.3px }
/* in the struck vendor column the strike runs through both figures: neither is what you pay */

/* ── the switch ── */
/* BIGGER, AND THE CHOICE IN BLUE (Lucky, 2026-10-01, 1.7.2): "change the toggle options to be a
   bit bigger, and the selected be in mashjoy.com blue instead of black … feel a bit interactive." */
.mj-fxswitch{ display:inline-flex; align-items:center; gap:2px; padding:3px;
  border:1px solid rgba(37,99,235,.28); border-radius:999px; background:var(--mj-trust-subtle);
  font-family:var(--font-meta); font-size:.74rem; font-weight:700; letter-spacing:.4px;
  text-transform:uppercase }
body.dark-mode .mj-fxswitch{ background:#17171a; border-color:#2a2a2d }
.mj-fxswitch__opt{ appearance:none; border:0; background:none; cursor:pointer;
  font:inherit; letter-spacing:inherit; text-transform:inherit; color:var(--mj-gray-400);
  padding:8px 16px; border-radius:999px; line-height:1; transition:background .15s, color .15s }
.mj-fxswitch__opt:hover{ color:var(--mj-trust) }
.mj-fxswitch__opt[aria-pressed="true"]{ background:var(--mj-trust); color:#fff;
  box-shadow:0 1px 3px rgba(37,99,235,.35) }
body.dark-mode .mj-fxswitch__opt[aria-pressed="true"]{ background:#3b7bff; color:#fff }
.mj-fxswitch__opt:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }

/* ── the basis line ── */
/* A block, not a flex row: the bold rate inside it is text, and a flex row made
   every text run and the <b> separate columns. The icon hangs in the padding. */
.mj-fxnote{ position:relative; display:block; margin:6px 0 0; padding-left:19px;
  font-family:var(--font-meta); font-size:.62rem; line-height:1.45; color:var(--mj-gray-400) }
.mj-fxnote::before{ content:""; position:absolute; left:0; top:1px; width:13px; height:13px;
  background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 8h13l-3-3M20 16H7l3 3'/%3E%3C/svg%3E") center/contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 8h13l-3-3M20 16H7l3 3'/%3E%3C/svg%3E") center/contain no-repeat }
.mj-fxnote b{ font-family:var(--font-display); font-weight:800; color:inherit }

/* ── the offer line ── */
.mj-deal{ display:flex; align-items:center; flex-wrap:wrap; gap:6px 8px; margin:8px 0 0;
  font-family:var(--font-body); font-size:.86rem; line-height:1.35 }
.mj-deal__kind{ display:inline-flex; align-items:center; gap:5px; flex:0 0 auto;
  font-family:var(--font-meta); font-size:.58rem; font-weight:700; letter-spacing:.6px;
  text-transform:uppercase; padding:3px 7px; border-radius:var(--radius-sm);
  color:var(--mj-growth-text); background:var(--mj-growth-subtle);
  border:1px solid rgba(5,122,83,.24) }
/* the icon comes from the shared [data-icon] rule: tag for a saving */
/* the VENDOR's own offer is blue (Lucky, 2026-10-01, 2.2: "make the … offer today element … in the
   mashjoy.com blue colour so that it can stand out"); our-link offers stay green */
.mj-deal[data-kind="public"] .mj-deal__kind{ color:var(--mj-trust); background:var(--mj-trust-subtle);
  border-color:rgba(37,99,235,.28) }
body.dark-mode .mj-deal__kind{ background:rgba(5,122,83,.16); border-color:rgba(5,122,83,.34) }
body.dark-mode .mj-deal[data-kind="public"] .mj-deal__kind{ color:#7aa7ff; background:rgba(59,123,255,.12);
  border-color:rgba(59,123,255,.32) }
.mj-deal__what b{ font-family:var(--font-display); font-weight:800 }
.mj-deal__code{ appearance:none; cursor:pointer; font-family:var(--font-display); font-weight:800;
  font-size:.72rem; letter-spacing:.8px; padding:3px 8px; border-radius:var(--radius-sm);
  border:1.5px dashed var(--mj-growth-text); color:var(--mj-growth-text); background:none }
.mj-deal__code[data-copied]::after{ content:" \2713 copied"; font-family:var(--font-meta);
  font-weight:700; font-size:.85em }
.mj-deal__ends{ display:inline-flex; align-items:center; gap:5px; font-family:var(--font-meta);
  font-size:.6rem; font-weight:700; letter-spacing:.5px; text-transform:uppercase;
  color:var(--mj-alert) }
.mj-deal__ends::before{ content:""; width:12px; height:12px; background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='13' r='8'/%3E%3Cpath d='M12 9v4l2.5 2M9 2h6'/%3E%3C/svg%3E") center/contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='13' r='8'/%3E%3Cpath d='M12 9v4l2.5 2M9 2h6'/%3E%3C/svg%3E") center/contain no-repeat }
.mj-deal[data-expired]{ display:none }
.mj-deal__src{ flex-basis:100%; font-family:var(--font-meta); font-size:.6rem; color:var(--mj-gray-400) }

s .mj-fxv__usd, s .mj-fxv__zar{ color:inherit }
/* ── a price cell in a table: both figures on ONE line, wrapping only when the column is narrow
   (2026-10-01; was two stacked lines with the estimate smaller underneath) ── */
.mj-tw__cell .mj-fxv{ flex-direction:row; align-items:baseline; gap:.4em; flex-wrap:nowrap; white-space:nowrap }
html.fx-zar .mj-tw__cell .mj-fxv{ flex-direction:row-reverse; justify-content:flex-end }
/* a price inside a spec row reads as part of that row's sentence: the row's own font and weight,
   the estimate grey (Lucky, 2026-10-01: prices in body ink, never a sticker in another face) */
.mj-pb__specs .mj-fxv{ display:inline-flex; gap:.28em; line-height:inherit }
.mj-pb__specs .mj-fxv__usd, .mj-pb__specs .mj-fxv__zar{ font:inherit; letter-spacing:inherit }
/* a plan card in the every-plan band */
.mj-pp__cp .mj-fxv{ font-size:inherit }
/* THE CURRENCY LINE, once per page under the byline (2026-09-24): the basis said once,
   and the switch within reach of a reader who never reaches a table. */
:root{ --mj-ico-fx:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 8h13l-3-3M20 16H7l3 3'/%3E%3C/svg%3E") }
.mj-fxbar{ position:relative; display:flex; flex-wrap:wrap; align-items:center; gap:8px 16px;
  margin:0 0 22px; padding:10px 12px 10px 34px; border:1px solid var(--mj-gray-200);
  border-radius:var(--radius-sm); background:var(--mj-surface-2); font-family:var(--font-meta);
  font-size:.74rem; line-height:1.45; color:var(--mj-gray-400) }
.mj-fxbar::before{ content:""; position:absolute; left:12px; top:13px; width:14px; height:14px;
  background:currentColor; -webkit-mask:var(--mj-ico-fx) center/contain no-repeat;
  mask:var(--mj-ico-fx) center/contain no-repeat }
.mj-fxbar__txt{ flex:1 1 300px }
/* Inside an article the prose rule for <p> outranks the element's own face: seen at
   1017px on 2026-09-24, the line rendered in the reading serif at body size. */
.mj-article p.mj-fxbar{ font-family:var(--font-meta); font-size:.74rem; line-height:1.45;
  color:var(--mj-gray-400); margin:0 0 22px }
.mj-fxbar__txt b{ color:var(--mj-ink,#141416) }
.mj-fxbar .mj-fxswitch{ flex:0 0 auto }
.mj-fxbar__ctl{ display:inline-flex; flex-direction:column; align-items:flex-start; gap:5px; flex:0 0 auto }
.mj-fxbar__k{ font-family:var(--font-meta); font-size:.66rem; font-weight:700; letter-spacing:.06em;
  text-transform:uppercase; color:var(--mj-trust) }
body.dark-mode .mj-fxbar__k{ color:#7aa7ff }
body.dark-mode .mj-fxbar{ background:rgba(255,255,255,.04); border-color:rgba(255,255,255,.12) }
body.dark-mode .mj-fxbar__txt b{ color:#e7e7ea }
/* DOLLAR FIGURES IN PROSE (2026-09-24): the build wraps every $ figure in running text.
   In dollars the rand estimate follows in brackets, quieter; in rand they trade places.
   The figure keeps the sentence's own face, weight and colour. */
.mj-fxv--inline{ display:inline-flex; gap:.28em; font:inherit; line-height:inherit; flex-wrap:nowrap }
.mj-fxv--inline .mj-fxv__usd, .mj-fxv--inline .mj-fxv__zar{ font:inherit; font-weight:inherit; letter-spacing:inherit }
.mj-fxv--inline .mj-fxv__usd{ color:inherit }
.mj-fxv--inline .mj-fxv__zar{ font-size:1em; color:var(--mj-gray-400) }
.mj-fxv--inline .mj-fxv__zar::before{ content:"(\2248\00a0" }
.mj-fxv--inline .mj-fxv__zar::after{ content:")" }
html.fx-zar .mj-fxv--inline .mj-fxv__zar{ font-size:1em; color:inherit }
body.dark-mode .mj-fxv--inline .mj-fxv__usd{ color:inherit }
html.fx-zar .mj-fxv--inline .mj-fxv__zar::before{ content:"\2248\00a0" }
html.fx-zar .mj-fxv--inline .mj-fxv__zar::after{ content:none }
html.fx-zar .mj-fxv--inline .mj-fxv__usd{ font-size:1em; color:var(--mj-gray-400) }
html.fx-zar .mj-fxv--inline .mj-fxv__usd::before{ content:"(" }
html.fx-zar .mj-fxv--inline .mj-fxv__usd::after{ content:")"; font:inherit; letter-spacing:inherit; text-transform:none }
/* A RAND DIFFERENCE THE BUILD COMPUTED ([[rand: 630 - $19]]): the page's own rate, so
   it always agrees with the estimates beside the prices. Never breaks across a line. */
.mj-fxcalc{ white-space:nowrap }
/* A struck price is one figure in the reader's chosen currency: a struck $23 beside
   ≈ R16 compares two currencies. */
s .mj-fxv{ font-size:inherit }
s .mj-fxv__zar, html.fx-zar s .mj-fxv__usd{ display:none }
html.fx-zar s .mj-fxv__zar{ display:inline; font-size:1em; color:inherit }


/* ── WHAT IT ACCEPTS · .mj-accepts (Lucky, c-2a8k, 2026-09-24) ─────────────────
   Payment methods a machine takes, as chips under the review block's verdict and
   under the product box's image. One glyph per kind, set by data-pm: tap (how it
   is taken), card (a network), wallet (a phone wallet), qr. Quiet by design: it
   answers a question, it does not sell. */
/* A BLUE PANEL (Lucky, 2026-10-01, 2.4): "make those stand out … a slightly bit of a blue
   mashjoy.com blue colour, and also the text that says accepts must also feel a bit attractive." */
.mj-accepts{ margin:12px 0 0 }
.mj-accepts__in{ display:block; padding:10px 12px; border:1px solid rgba(37,99,235,.18);
  border-radius:var(--radius-sm); background:var(--mj-trust-subtle) }
.mj-accepts__k{ display:flex; align-items:center; gap:6px; margin:0 0 8px; font-family:var(--font-display);
  font-size:.72rem; font-weight:800; letter-spacing:.04em; text-transform:uppercase; color:var(--mj-trust) }
.mj-accepts__k::before{ content:""; width:14px; height:14px; background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='5' width='20' height='14' rx='2'/%3E%3Cpath d='M2 10h20'/%3E%3C/svg%3E") center/contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='5' width='20' height='14' rx='2'/%3E%3Cpath d='M2 10h20'/%3E%3C/svg%3E") center/contain no-repeat }
body.dark-mode .mj-accepts__in{ background:rgba(59,123,255,.08); border-color:rgba(59,123,255,.24) }
body.dark-mode .mj-accepts__k{ color:#7aa7ff }
.mj-accepts__list{ display:flex; flex-wrap:wrap; gap:6px; list-style:none; margin:0; padding:0 }
.mj-accepts__i{ display:inline-flex; align-items:center; gap:5px; margin:0; padding:3px 9px 3px 7px;
  border:1px solid rgba(37,99,235,.22); border-radius:999px; background:var(--mj-white);
  font-family:var(--font-meta); font-size:.72rem; font-weight:600; line-height:1.3; color:var(--mj-ink) }
.mj-accepts__i::before{ content:""; flex:none; width:13px; height:13px; background:currentColor; opacity:.7;
  -webkit-mask:var(--mj-pm) center/contain no-repeat; mask:var(--mj-pm) center/contain no-repeat }
.mj-accepts__i[data-pm="tap"]{ --mj-pm:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M8.5 7.5a6 6 0 0 1 0 9M12 5a9.5 9.5 0 0 1 0 14M15.5 2.5a13 13 0 0 1 0 19M5 10a2.5 2.5 0 0 1 0 4'/%3E%3C/svg%3E") }
.mj-accepts__i[data-pm="card"]{ --mj-pm:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.5' y='5' width='19' height='14' rx='2.5'/%3E%3Cpath d='M2.5 10h19M6.5 15h4'/%3E%3C/svg%3E") }
.mj-accepts__i[data-pm="wallet"]{ --mj-pm:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='6.5' y='2' width='11' height='20' rx='2.5'/%3E%3Cpath d='M11 18.5h2'/%3E%3C/svg%3E") }
.mj-accepts__i[data-pm="qr"]{ --mj-pm:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='7' height='7'/%3E%3Crect x='14' y='3' width='7' height='7'/%3E%3Crect x='3' y='14' width='7' height='7'/%3E%3Cpath d='M14 14h3v3h-3zM18 18h3v3h-3z'/%3E%3C/svg%3E") }
.mj-accepts__note{ margin:6px 0 0; font-family:var(--font-meta); font-size:.66rem; color:var(--mj-text-muted) }
.mj-pb .rank-col .mj-accepts{ margin-top:10px }
body.dark-mode .mj-accepts__i{ background:#141414; border-color:#2a2a2d; color:#e8e8ea }


/* ── BUSINESS-TYPE PICKER · .mj-bizfit (Lucky, c-ene9, 2026-09-24) ─────────────
   One tap, one answer. The finder's frame and head, so the two read as family;
   chips instead of questions; one answer card whose badge colour carries the
   verdict (green strong, blue good, red probably not). */
.mj-bizfit{ max-width:var(--article-width); margin:var(--sp-6) auto; border:1px solid var(--mj-rule);
  border-top:3px solid var(--mj-red); border-radius:var(--radius-md); background:#fff; overflow:hidden;
  box-shadow:0 1px 2px rgba(10,10,10,.04), 0 8px 24px -12px rgba(10,10,10,.10) }
body.dark-mode .mj-bizfit{ background:#161618; border-color:#2a2a2d; border-top-color:var(--mj-red); box-shadow:none }
.mj-bizfit__head{ padding:var(--sp-5) var(--sp-5) var(--sp-4);
  background:linear-gradient(180deg, rgba(236,34,39,.045), rgba(236,34,39,0)) }
.mj-bizfit__k{ display:inline-flex; align-items:center; gap:7px; font-family:var(--font-display); font-weight:700;
  font-size:.62rem; letter-spacing:1.4px; text-transform:uppercase; color:var(--mj-red);
  background:rgba(236,34,39,.08); border:1px solid rgba(236,34,39,.22); border-radius:999px;
  padding:5px 11px; margin:0 0 9px }
.mj-bizfit__h{ font-family:var(--font-display); font-weight:800; font-size:1.24rem; line-height:1.25;
  color:var(--mj-black); margin:0 0 5px }
.mj-bizfit__sub{ font-size:.92rem; line-height:1.55; color:var(--mj-text-muted); margin:0 }
body.dark-mode .mj-bizfit__h{ color:#fff }
body.dark-mode .mj-bizfit__sub{ color:#b6b6bb }
.mj-bizfit__types{ display:flex; flex-wrap:wrap; gap:8px; padding:0 var(--sp-5) var(--sp-4) }
.mj-bizfit__type{ font-family:var(--font-display); font-weight:700; font-size:.8rem; line-height:1.2;
  padding:9px 14px; border:1px solid var(--mj-gray-300); border-radius:999px; background:#fff;
  color:var(--mj-ink); cursor:pointer; transition:border-color .15s, background .15s, color .15s }
.mj-bizfit__type:hover{ border-color:var(--mj-ink) }
.mj-bizfit__type:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
.mj-bizfit__type[aria-selected="true"]{ background:var(--mj-ink); border-color:var(--mj-ink); color:#fff }
body.dark-mode .mj-bizfit__type{ background:#1d1d20; border-color:#3a3a3e; color:#e8e8ea }
body.dark-mode .mj-bizfit__type[aria-selected="true"]{ background:#fff; border-color:#fff; color:#111 }
.mj-bizfit__panels{ border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-bizfit__panels{ border-color:#242427 }
.mj-bizfit__panel{ padding:var(--sp-5) }
.mj-bizfit__panel + .mj-bizfit__panel{ border-top:1px solid var(--mj-rule) }
/* inserts.js hides the unselected answers; without it every answer stays readable. */
.mj-bizfit__panel[data-folded]{ display:none }
.mj-bizfit__fit{ display:inline-block; margin:0 0 10px; padding:4px 11px; border-radius:999px;
  font-family:var(--font-display); font-weight:700; font-size:.7rem; letter-spacing:1px; text-transform:uppercase }
.mj-bizfit__fit[data-fit-level="strong"]{ color:#047857; background:rgba(16,185,129,.12) }
.mj-bizfit__fit[data-fit-level="good"]{ color:#1d4ed8; background:rgba(37,99,235,.10) }
.mj-bizfit__fit[data-fit-level="no"]{ color:#b91c1c; background:rgba(236,34,39,.10) }
body.dark-mode .mj-bizfit__fit[data-fit-level="strong"]{ color:#34d399 }
body.dark-mode .mj-bizfit__fit[data-fit-level="good"]{ color:#93c5fd }
body.dark-mode .mj-bizfit__fit[data-fit-level="no"]{ color:#fca5a5 }
.mj-bizfit__why{ font-size:1.02rem; line-height:1.6; color:var(--mj-ink); margin:0 0 var(--sp-4) }
body.dark-mode .mj-bizfit__why{ color:#ececee }
.mj-bizfit__facts{ display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:0 0 var(--sp-4) }
.mj-bizfit__fact{ margin:0; padding:11px 13px; border:1px solid var(--mj-rule); border-radius:var(--radius-sm, 6px);
  background:var(--mj-surface-alt) }
body.dark-mode .mj-bizfit__fact{ background:#1b1b1e; border-color:#2a2a2d }
.mj-bizfit__fact dt{ font-family:var(--font-meta); font-size:.62rem; font-weight:700; letter-spacing:.12em;
  text-transform:uppercase; color:var(--mj-gray-400); margin:0 0 4px }
.mj-bizfit__fact dd{ margin:0; font-size:.9rem; line-height:1.45; color:var(--mj-ink); font-weight:600 }
body.dark-mode .mj-bizfit__fact dd{ color:#ececee }
.mj-bizfit__cta{ display:inline-block; font-family:var(--font-display); font-weight:700; font-size:.78rem;
  letter-spacing:1px; text-transform:uppercase; text-decoration:none; color:#fff; background:var(--mj-red);
  padding:12px 20px; border-radius:var(--radius-sm, 6px) }
.mj-bizfit__cta:hover{ background:var(--mj-red-hover) }
.mj-bizfit__note{ margin:0; padding:var(--sp-3) var(--sp-5); border-top:1px solid var(--mj-rule);
  font-family:var(--font-meta); font-size:.7rem; color:var(--mj-text-muted) }
body.dark-mode .mj-bizfit__note{ border-color:#242427; color:#a4a4aa }
/* On a phone the chips WRAP rather than scroll: a sideways row hid the last
   business type off-screen with nothing to say it was there. */
@media (max-width:600px){
  .mj-bizfit__type{ font-size:.76rem; padding:8px 12px }
  .mj-bizfit__facts{ grid-template-columns:1fr }
}


/* THE PICTURE SAYS WHOSE IT IS (2026-09-24). Studio sets `provenance` and
   `credit` on a review block's image; until now the block drew neither, so a
   supplier's product shot sat unlabelled. The label rides the image's corner,
   as small as the credit on a newspaper photograph. */
.scms-rb--full .rb-f-visual{ position:relative }
.rb-f-credit{ position:absolute; right:6px; bottom:6px; margin:0; padding:2px 7px; border-radius:4px;
  background:rgba(255,255,255,.88); font-family:var(--font-meta); font-size:.58rem; letter-spacing:.02em;
  color:var(--mj-text-muted); pointer-events:none }
body.dark-mode .rb-f-credit{ background:rgba(20,20,22,.85); color:#b6b6bb }


/* ── THE FEE SCALE SAYS WHERE EACH ONE WINS (Lucky, c-0x01, 2026-09-24) ─────────
   A slider ranks on one axis, so the provider that loses on the rate looked like
   it loses, full stop. Each row now carries its own edge in one line, a provider
   that cannot do the reader's scenario says "not offered" instead of pricing a
   thing it does not sell, and a quote-only company is named under the rows with
   its reason and a way to ask, rather than silently missing. */
.mj-fscale__edge{ display:block; margin-top:3px; font-family:var(--font-body, inherit); font-weight:400;
  font-size:.74rem; line-height:1.35; color:var(--mj-text-muted); text-transform:none; letter-spacing:0 }
.mj-fscale__edge:empty{ display:none }
body.dark-mode .mj-fscale__edge{ color:#a4a4aa }
.mj-fscale__row.is-na .mj-fscale__fee{ color:var(--mj-gray-400); font-size:.78rem; font-weight:700 }
.mj-fscale__row.is-na .mj-fscale__fill{ width:0 !important }
.mj-fscale__quotes{ list-style:none; margin:var(--sp-4) 0 0; padding:var(--sp-3) 0 0;
  border-top:1px dashed var(--mj-rule); display:grid; gap:6px }
body.dark-mode .mj-fscale__quotes{ border-color:#2a2a2d }
.mj-fscale__quote{ margin:0; font-size:.84rem; line-height:1.5; color:var(--mj-text-muted) }
.mj-fscale__qname{ font-family:var(--font-display); font-weight:700; color:var(--mj-ink) }
body.dark-mode .mj-fscale__qname{ color:#fff }
.mj-fscale__qwhy{ color:inherit }
.mj-fscale__ask{ font-family:var(--font-display); font-weight:700; font-size:.78rem; white-space:nowrap;
  color:var(--mj-trust); text-decoration:underline; text-underline-offset:3px }

/* ── PLANS: "where to start" (Lucky, 2026-10-01, 2.2) ──────────────────────────
   Under the plan cards: what every plan already includes (a fact from the record), and where
   THIS article's reader should start (from the article's box), so the tray ends on advice. */
.mj-pp__start{ margin:12px 28px 0; padding:10px 12px; border-left:3px solid var(--mj-gray-300, #d4d4d8);
  background:var(--mj-surface, #fff); border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  font-family:var(--font-body); font-size:.9rem; line-height:1.5; color:var(--mj-ink) }
.mj-pp__all, .mj-pp__go{ display:block; margin:0 }
.mj-pp__go{ margin-top:4px }
.mj-pp__startk{ font-family:var(--font-display); font-weight:800; color:var(--mj-ink) }
.mj-pp__allv, .mj-pp__gov{ color:inherit }
.mj-pp__allk, .mj-pp__gok{ margin-right:2px }
body.dark-mode .mj-pp__start{ background:rgba(255,255,255,.04); color:#e7e7ea }
body.dark-mode .mj-pp__startk{ color:inherit }
@media (max-width:720px){ .mj-pp__start{ margin:12px var(--sp-5) 0 } }   /* aligned with the plan cards */

/* A WHITE-ONLY VENDOR MARK, WHEREVER IT IS DRAWN (J-2, 2026-10-01). The product box has its
   own dark well (`data-kind="logo-light"`); rails, tables and CTAs reuse the same file, so a
   white mark carries its own dark ground everywhere else and is never invisible. */
img[src*="--logo--white"]{ background:#141416; padding:4px 8px; border-radius:4px; box-sizing:content-box }
.mj-pb__img[data-kind="logo-light"] img[src*="--logo--white"]{ background:none; padding:0 }

/* ── HOMEPAGE V2 FOUNDATIONS (Lucky, 2026-10-01) ─────────────────────────────
   1 · SECTION TITLES IN mashjoy.com BLUE. Red already carries every kicker, so a
       page whose only colour is red let the kickers shout over the section names
       that are meant to guide. Blue names the section; red stays on the kicker. */
.mj-nx-head__t, .mj-hp-rule h2, .mj-nx-rail__h{ color:var(--mj-trust) }
body.dark-mode .mj-nx-head__t, body.dark-mode .mj-hp-rule h2,
body.dark-mode .mj-nx-rail__h{ color:#7aa2ff }
/* 2 · MORE AIR BETWEEN MAIN SECTIONS — the Newsreader news demo gives each section
       room to end before the next begins. 48px under the content, the thin grey
       rule, 64px before the next title (was 32 + 48). */
.mj-nx{ margin-bottom:var(--sp-8); padding-bottom:var(--sp-7) }
@media(max-width:599px){ .mj-nx{ margin-bottom:var(--sp-7); padding-bottom:var(--sp-6) } }
/* 3 · A WHITE PAGE for the homepage, archives and articles: white reads as serious
       and lets pictures and the near-white panels (#FDFDFD) separate from it. The
       legacy cream body rule (#FAF9F6) is overridden in light mode only. */
body:not(.dark-mode):not(.dark){ background-color:#FFFFFF }
body:not(.dark-mode):not(.dark) .mj-sum,
body:not(.dark-mode):not(.dark) .a-disclose{ background-color:#FDFDFD }

/* SMART CROPS (2026-10-01). site/crops.py gives every media <img> custom properties
   per slot shape: --o-<shape> (where to crop, the most-detailed window) and, for wide
   designed banners, --f-<shape>:contain (show the whole picture) on --crop-bg (its
   own edge colour). These rules say which shape each slot is. Shapes: sq 1:1 ·
   w 16:9 · t 16:10 · r 3:2 · q 4:3 · p 3:4. Without the properties every rule falls
   back to a centred cover crop, so a page built without crops.py looks as before. */
.mj-nx-pic img{ object-fit:var(--f-w, cover); object-position:var(--o-w, 50% 50%);
  background-color:var(--crop-bg, transparent) }
.mj-nx-pic--sq img, .mj-nx-row .mj-nx-pic img, .mj-nx-num .mj-nx-pic img{
  object-fit:var(--f-sq, cover); object-position:var(--o-sq, 50% 50%) }
.mj-nx-row--right .mj-nx-pic img{ object-fit:var(--f-r, cover); object-position:var(--o-r, 50% 50%) }
.mj-nx-pic--43 img{ object-fit:var(--f-q, cover); object-position:var(--o-q, 50% 50%) }
.mj-nx-tile img, .mj-nx-city__lead .mj-nx-pic img{ object-fit:var(--f-t, cover);
  object-position:var(--o-t, 50% 50%); background-color:var(--crop-bg, #1f1f24) }
.mj-nx-tile--tall img{ object-fit:var(--f-p, cover); object-position:var(--o-p, 50% 50%) }
.mj-nx-tile--big img{ object-fit:var(--f-w, cover); object-position:var(--o-w, 50% 50%) }
/* older index templates still in use until their archives are rebuilt (phases 6–8) */
.mj-nl-sub__img img{ object-fit:var(--f-t, cover); object-position:var(--o-t, 50% 50%);
  background-color:var(--crop-bg, transparent) }
.mj-desk__th img{ object-fit:var(--f-q, cover); object-position:var(--o-q, 50% 50%);
  background-color:var(--crop-bg, transparent) }

/* EXCERPTS WHERE A TITLE STOOD ALONE (2026-10-01). Side picture cards, Most-discussed
   cards and picture-right rows print the story's dek, clamped so a long one never
   makes a card taller than its neighbours. The lead's dek is never clamped. */
.mj-nx-card:not(.mj-nx-card--lead) .mj-nx-d, .mj-nx-row .mj-nx-d{ font-size:.9rem; line-height:1.5;
  margin-top:6px; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden }
.mj-nx-row .mj-nx-d{ -webkit-line-clamp:3 }
@media(max-width:599px){ .mj-nx-row--right .mj-nx-d{ -webkit-line-clamp:2 } }

/* ── HOMEPAGE V2 · THE FURNITURE AS STORIES (5b-2, 2026-10-01) ──────────────────
   Lucky: the page "breaks into another theme where it's more geeky … more tools-like".
   Below the desks every block is now made of the same atoms as the sections above it:
   pictures where a story exists, words only where none does, and no coloured bars
   down the side — a block stands out through its own words and pictures. */

/* THE TURN — "We changed our mind" as a story. Picture from the article whose verdict
   changed; what we said, struck through; what we say now, as the headline; why, as the
   excerpt. */
.mj-nx-turns{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--sp-7) }
.mj-nx-turn{ display:grid; grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr); gap:var(--sp-5);
  align-items:start }
.mj-nx-turn .mj-nx-pic{ aspect-ratio:3/2 }
.mj-nx-turn__b{ min-width:0 }
.mj-nx-turn .mj-nx-pic img{ object-fit:var(--f-r, cover); object-position:var(--o-r, 50% 50%) }
.mj-nx-turn__l{ display:block; margin-bottom:3px; font-family:var(--font-meta); font-weight:700;
  font-size:.64rem; letter-spacing:1.3px; text-transform:uppercase; color:var(--mj-text-muted) }
.mj-nx-turn__was{ margin:2px 0 12px; font-family:var(--font-display); font-weight:700; font-size:.95rem;
  line-height:1.3; color:var(--mj-text-muted) }
.mj-nx-turn__was s{ text-decoration-color:var(--mj-red); text-decoration-thickness:2px }
.mj-nx-turn__now{ font-size:1.3rem; line-height:1.18 }
.mj-nx-turn__now .mj-nx-turn__l{ color:var(--mj-trust) }
body.dark-mode .mj-nx-turn__now .mj-nx-turn__l{ color:#7aa2ff }
.mj-nx-turn .mj-nx-d{ display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:4; overflow:hidden;
  font-size:.95rem }

/* START HERE — three picture cards. BY YOUR BUSINESS — the business, as picture tiles. */
.mj-nx-start__g{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--sp-5) }
.mj-nx-biz__g{ display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:var(--sp-4) }
.mj-nx-biz__g .mj-nx-tile{ aspect-ratio:4/5 }
.mj-nx-biz__g .mj-nx-tile__b{ padding:16px 18px }
.mj-nx-biz__g .mj-nx-tile img{ object-fit:var(--f-p, cover); object-position:var(--o-p, 50% 50%) }
.mj-nx-biz__g .mj-nx-t{ font-size:1.15rem }

/* TEXT GRIDS — Answered this week, Across the desk, Desks opening next. */
.mj-nx-texts{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); column-gap:var(--sp-5) }
.mj-nx-texts--6{ grid-template-columns:repeat(6,minmax(0,1fr)) }
.mj-nx-texts .mj-nx-d{ margin-top:4px; font-size:.92rem }
.mj-nx-texts .mj-nx-by:empty{ display:none }

/* the atoms exhibit: a wide atom takes the whole row (prototype only) */
.mj-nx-atoms__i--wide{ grid-column:1 / -1 }

@media(max-width:1179px){
  .mj-nx-turns{ grid-template-columns:1fr; gap:var(--sp-6) }
  .mj-nx-biz__g .mj-nx-tile{ aspect-ratio:3/4 }
  .mj-nx-biz__g .mj-nx-t{ font-size:1rem }
  .mj-nx-biz__g .mj-nx-tile__b{ padding:12px 14px }
  .mj-nx-texts--6{ grid-template-columns:repeat(3,minmax(0,1fr)) }
}
@media(max-width:599px){
  .mj-nx-turn{ grid-template-columns:1fr; gap:var(--sp-3) }
  .mj-nx-start__g, .mj-nx-texts{ grid-template-columns:1fr }
  .mj-nx-texts--6{ grid-template-columns:repeat(2,minmax(0,1fr)) }
  /* on a phone Start here reads as thumbed rows, like the City three */
  .mj-nx-start__g > .mj-nx-card{ display:grid; grid-template-columns:96px minmax(0,1fr); gap:14px;
    align-items:start; padding-top:14px; border-top:1px solid var(--mj-rule) }
  .mj-nx-start__g > .mj-nx-card .mj-nx-pic{ aspect-ratio:1/1; margin-bottom:0 }
  .mj-nx-start__g > .mj-nx-card .mj-nx-pic img{ object-fit:var(--f-sq, cover); object-position:var(--o-sq, 50% 50%) }
  .mj-nx-start__g{ row-gap:14px }
}
/* FIVE BUSINESSES NEVER LEAVE A HOLE: five across down to 700px; below, two across with
   the odd one out spanning the row as a wide tile. */
@media(max-width:699px){
  .mj-nx-biz__g{ grid-template-columns:repeat(2,minmax(0,1fr)) }
  .mj-nx-biz__g .mj-nx-tile{ aspect-ratio:1/1 }
  .mj-nx-biz__g > .mj-nx-tile:last-child:nth-child(odd){ grid-column:1 / -1; aspect-ratio:2/1 }
  .mj-nx-biz__g > .mj-nx-tile:last-child:nth-child(odd) img{ object-fit:var(--f-w, cover); object-position:var(--o-w, 50% 50%) }
}

/* THE SECTION'S OWN NAME. Every homepage section carries one, so a link can land on it
   (`#…`) without the pinned header covering its title, and so each block is addressable
   in the library. */
.mj-nx-front, .mj-nx-city, .mj-nx-quad, .mj-nx-news, .mj-nx-latest, .mj-nx-turnset, .mj-nx-start, .mj-nx-biz, .mj-nx-qa, .mj-nx-across,
.mj-nx-next, .mj-nx-innov, .mj-nx-pol, .mj-nx-gadgets, .mj-nx-disc{ scroll-margin-top:calc(var(--mj-header-top, 76px) + 16px) }

/* ── THE PLANS DEAL PANEL · `.mj-dp` (Lucky, 2026-10-01) ─────────────────────────────
   For a software or service brand with a plans record and a tracked link, the box's lower
   half becomes one wide panel: the price and the vendor's offer side by side, every plan as a
   card that opens that company's plans page through our link, our pick to start, what to move
   to, and the box's own dark CTA foot to close. Lead-form brands keep their quote foot. */
.mj-pb--rank > .mj-pb__plans:has(> .mj-dp){ border:0; padding:0; margin:10px -28px -28px }
.mj-pb--rank:has(> .mj-pb__plans > .mj-dp){ padding-bottom:28px }
.mj-dp{ padding:22px 28px 0; border-top:1px solid var(--mj-rule);
  background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 5%, #fff) }
body.dark-mode .mj-dp{ background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 7%, #141416) }
/* the price and the offer, side by side, each a card at full width */
.mj-dp__top{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:12px; align-items:stretch }
.mj-dp__top > .mj-pb__pay, .mj-dp__top > .mj-deal{ margin:0; padding:16px 18px; background:#fff;
  border:1px solid var(--mj-rule); border-radius:var(--radius-md) }
body.dark-mode .mj-dp__top > .mj-pb__pay, body.dark-mode .mj-dp__top > .mj-deal{ background:#1b1b1f; border-color:#2c2c33 }
.mj-dp__top > .mj-pb__pay{ display:flex; align-items:center; border-left:4px solid var(--brand-accent, var(--mj-growth)) }
.mj-dp__top > .mj-pb__pay .mj-pb__pay-col b,
.mj-dp__top > .mj-pb__pay:not(:has(.mj-pb__pay-col--yours)) .mj-pb__pay-col b{ font-size:2.1rem; line-height:1.1 }
.mj-dp__top > .mj-pb__pay .mj-pb__pay-src{ font-size:.68rem; line-height:1.5; margin-top:4px }
.mj-dp__top .mj-pb__pay-tag:empty{ display:none }
.mj-dp__top > .mj-deal{ display:flex; flex-direction:column; justify-content:center; align-items:flex-start; gap:6px }
.mj-dp__top > .mj-deal .mj-deal__src{ flex-basis:auto }
.mj-dp__top > .mj-deal .mj-deal__what{ font-size:1.02rem }
.mj-dp__top > .mj-deal .mj-deal__what b{ font-size:1.25rem }
.mj-dp__top > .mj-deal:only-child{ grid-column:1 / -1 }
/* the plans */
.mj-dp__head{ margin:22px 0 12px }
.mj-dp__t{ margin:0; font-family:var(--font-display); font-weight:800; font-size:1.08rem; color:var(--mj-ink) }
.mj-dp__s{ margin:2px 0 0; font-size:.88rem; color:var(--mj-gray-500, #6b6b72) }
.mj-dp__plans{ display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:12px; align-items:stretch }
.mj-dp__plan{ position:relative; display:flex; flex-direction:column; gap:8px; padding:18px 16px 14px;
  background:#fff; border:1px solid var(--mj-rule); border-top:4px solid var(--brand-accent, var(--mj-growth));
  border-radius:var(--radius-md); color:var(--mj-ink); text-decoration:none;
  transition:transform .15s ease, box-shadow .15s ease, border-color .15s ease }
.mj-dp__plan:hover, .mj-dp__plan:focus-visible{ transform:translateY(-3px); box-shadow:0 10px 24px rgba(20,20,22,.12);
  border-color:var(--brand-accent, var(--mj-growth)) }
.mj-dp__plan:focus-visible{ outline:3px solid var(--mj-trust); outline-offset:2px }
.mj-dp__plan[data-pick="1"]{ border-width:2px 2px 4px; border-style:solid; border-color:var(--brand-accent, var(--mj-growth));
  box-shadow:0 6px 18px rgba(20,20,22,.08) }
body.dark-mode .mj-dp__plan{ background:#1b1b1f; border-color:#2c2c33; color:#ececf0 }
.mj-dp__badge{ align-self:flex-start; margin:-4px 0 2px; padding:3px 9px; border-radius:999px;
  background:var(--brand-accent, var(--mj-growth)); color:var(--brand-ink, #141416);
  font-family:var(--font-display); font-weight:800; font-size:.66rem; letter-spacing:.06em; text-transform:uppercase }
.mj-dp__pn{ font-family:var(--font-display); font-weight:800; font-size:1.02rem }
.mj-dp__pp{ display:flex; align-items:baseline; flex-wrap:wrap; gap:6px }
.mj-dp__ppv{ font-family:var(--font-display); font-weight:800; font-size:1.5rem; line-height:1.15 }
.mj-dp__ppu{ font-size:.86rem; color:var(--mj-gray-500, #6b6b72) }
.mj-dp__fit{ display:block; margin:2px 0 4px; padding:9px 11px; border-radius:var(--radius-sm);
  background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 10%, #fff); font-size:.88rem; line-height:1.45 }
body.dark-mode .mj-dp__fit{ background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 16%, #1b1b1f) }
.mj-dp__fitk{ display:block; font-family:var(--font-display); font-weight:800; font-size:.68rem; letter-spacing:.06em;
  text-transform:uppercase; margin-bottom:2px }
.mj-dp__gk, .mj-dp__kk{ display:block; font-family:var(--font-display); font-weight:800; font-size:.66rem;
  letter-spacing:.08em; text-transform:uppercase; color:var(--mj-ink) }
/* WHAT TO NOTE IS NOTICED (Lucky, J-7b, 2026-10-04): grey read as an afterthought. One accent that works on
   every vendor's card, including yellow ones: mashjoy.com blue, for the label and its "i" markers. */
.mj-dp__kk{ color:var(--mj-trust) }
.mj-dp__get, .mj-dp__know{ list-style:none; margin:0; padding:0; display:grid; gap:5px }
.mj-dp__gi, .mj-dp__ki{ position:relative; padding-left:22px; font-size:.86rem; line-height:1.4 }
.mj-dp__gi::before{ content:""; position:absolute; left:0; top:2px; width:15px; height:15px; border-radius:50%;
  background:var(--mj-growth) }
.mj-dp__gi::after{ content:""; position:absolute; left:3px; top:5px; width:9px; height:9px; background:#fff;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.2 4.2L19 7'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.2 4.2L19 7'/%3E%3C/svg%3E") center/contain no-repeat }
.mj-dp__ki::before{ content:"i"; position:absolute; left:0; top:2px; width:15px; height:15px; border-radius:50%;
  background:var(--mj-trust); color:#fff; font:800 .62rem/15px var(--font-display); text-align:center }
body.dark-mode .mj-dp__ki::before{ background:#5b8cff; color:#0c0c0e }
.mj-dp__go{ margin-top:auto; padding-top:10px; border-top:1px solid var(--mj-rule); display:flex; align-items:center;
  justify-content:space-between; gap:8px; font-family:var(--font-display); font-weight:800; font-size:.78rem;
  letter-spacing:.04em; text-transform:uppercase }
.mj-dp__go::after{ content:"\2192"; font-size:1rem; transition:transform .15s ease }
.mj-dp__plan:hover .mj-dp__go::after{ transform:translateX(4px) }
/* where to start, where to go next, and what every plan has */
.mj-dp__advice{ margin:16px 0 0; padding:14px 18px; background:#fff; border:1px solid var(--mj-rule);
  border-radius:var(--radius-md); display:grid; gap:8px }
body.dark-mode .mj-dp__advice{ background:#1b1b1f; border-color:#2c2c33 }
.mj-dp__adv{ margin:0; font-size:.92rem; line-height:1.5 }
.mj-dp__advk{ font-family:var(--font-display); font-weight:800; margin-right:4px }
.mj-dp__adv--start .mj-dp__advk{ color:var(--brand-accent-text, var(--mj-ink)) }
.mj-dp__adv--all{ padding-top:8px; border-top:1px solid var(--mj-rule); font-size:.86rem; color:var(--mj-gray-600, #55555b) }
/* the close: the box's own dark CTA foot, full width */
.mj-dp .mj-pb__foot{ margin:18px -28px 0; border-radius:0 }
.mj-dp .mj-pb__footin, .mj-dp .mj-dp__foot2{ flex:1 1 100% }
.mj-dp__foot2{ margin:0; padding:0 28px 18px; display:flex; flex-wrap:wrap; align-items:center; gap:8px 18px;
  font-size:.82rem; color:#c9c9cf }
.mj-dp__review{ color:#fff; font-weight:600; text-decoration:underline; text-underline-offset:3px }
.mj-dp__note{ margin:0; padding:10px 0 0; font-family:var(--font-meta); font-size:.64rem; color:var(--mj-gray-500, #6b6b72) }
@media (max-width:720px){
  .mj-pb--rank > .mj-pb__plans:has(> .mj-dp){ margin:10px calc(-1 * var(--sp-5)) calc(-1 * var(--sp-5)) }
  .mj-dp{ padding:18px var(--sp-5) 0 }
  .mj-dp__top{ grid-template-columns:1fr }
  .mj-dp__plan[data-pick="1"]{ order:-1 }   /* on a phone, our pick is the first plan you meet */
  .mj-dp .mj-pb__foot{ margin:18px calc(-1 * var(--sp-5)) 0 }
  .mj-dp__foot2{ padding:0 var(--sp-5) 18px }
}
/* label/value hooks for the template; styling lives on .mj-dp__advk / .mj-dp__advv */
.mj-dp__startk, .mj-dp__growk, .mj-dp__allk, .mj-dp__alsok, .mj-dp__startv, .mj-dp__growv, .mj-dp__allv, .mj-dp__alsov{ }

/* ── BREAK BANDS (5b-3, Lucky 2026-10-01) ────────────────────────────────────────
   "news, then a banner, then another news-centric section … make use of the black and
   other mixture colours … so that the brand can stand out a little bit more and feel more
   human". Four bands between the news sections, never two in a row: DEALS (black, red
   money), FREE TOOLS (black, raised cards), SMART GUIDES (warm paper, covers), THE NEWSLETTER (brand
   red). One structure — the band's name on the left, its content on the right — so the
   four read as one family and differ only in colour and in what they carry.
   FULL-BLEED WITHOUT WIDENING THE PAGE: the colour is painted past the content box with a
   box-shadow and clipped back to the band's own height, so the layout width never exceeds
   the viewport (100vw would, by the width of a Windows scrollbar). */
.mj-band{ --band:#0A0A0A; --band-ink:#fff; --band-k:#ff5a5f; --band-muted:rgba(255,255,255,.66);
  --band-line:rgba(255,255,255,.16); position:relative; margin:0 0 var(--sp-8);
  padding:56px 0; background:var(--band); color:var(--band-ink);
  box-shadow:0 0 0 100vmax var(--band); clip-path:inset(0 -100vmax) }
.mj-nx:has(+ .mj-band){ border-bottom:0; padding-bottom:0 }
.mj-band__in{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,2.5fr); gap:var(--sp-7);
  align-items:start }
.mj-band__head{ min-width:0 }
.mj-band__k{ margin:0 0 10px; font-family:var(--font-meta); font-weight:700; font-size:.68rem;
  letter-spacing:1.6px; text-transform:uppercase; color:var(--band-k) }
.mj-band__t{ margin:0; font-family:var(--font-display); font-weight:800; font-size:clamp(1.5rem, 2.4vw, 2.1rem);
  line-height:1.12; color:var(--band-ink) }
.mj-band__d{ margin:14px 0 0; font-family:var(--font-body); font-size:1rem; line-height:1.6; color:var(--band-muted) }
.mj-band__all{ display:inline-block; margin-top:18px; font-family:var(--font-meta); font-size:.78rem;
  color:var(--band-ink); text-decoration:underline; text-underline-offset:4px }
.mj-band__all::after{ content:" ›" }
.mj-band__all:hover{ color:var(--band-k) }
.mj-band__btn{ display:inline-block; margin-top:22px; padding:14px 22px; font-family:var(--font-meta);
  font-weight:700; font-size:.8rem; letter-spacing:1px; text-transform:uppercase; text-decoration:none;
  color:#fff; background:#0A0A0A; border-radius:2px }
.mj-band__btn:hover{ background:#26262b }

/* DEALS — black; the money in the display face, in red. `.mj-bdeal`, not `.mj-deal`: that
   name is the vendor-offer line inside every product box (renderers._deal_line). */
.mj-band[data-band="deals"] .mj-band__body{ display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:var(--sp-5) }
.mj-bdeal{ display:flex; flex-direction:column; align-items:flex-start; gap:8px; padding:24px 26px; text-align:left; text-decoration:none;
  color:var(--band-ink); border:1px solid var(--band-line); border-radius:2px; transition:border-color .2s }
.mj-bdeal:hover{ border-color:var(--band-k) }
.mj-bdeal__amt{ font-family:var(--font-display); font-weight:800; font-size:3rem; line-height:1; color:var(--band-k) }
.mj-bdeal__off{ font-size:1.2rem; color:var(--band-ink) }
.mj-bdeal__what{ font-family:var(--font-display); font-weight:700; font-size:1.02rem; line-height:1.3 }
.mj-bdeal__by{ font-family:var(--font-meta); font-size:.74rem; color:var(--band-muted) }
.mj-bdeal__go{ margin-top:auto; padding-top:10px; font-family:var(--font-meta); font-size:.78rem; font-weight:700;
  color:var(--band-ink); text-decoration:underline; text-underline-offset:4px }

/* FREE TOOLS — black (Lucky, 2026-10-03: "navy is not a mashjoy.com colour"). Told apart from
   the deals band by its cards: raised panels on the black, where deals are outlined. */
.mj-band[data-band="tools"]{ --band:#0A0A0A; --band-k:#ff5a5f; --band-muted:rgba(255,255,255,.68); --band-line:rgba(255,255,255,.12) }
.mj-band[data-band="tools"] .mj-band__body{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--sp-4) }
.mj-tool{ display:flex; flex-direction:column; gap:6px; padding:20px 22px; text-decoration:none; color:var(--band-ink);
  background:rgba(255,255,255,.06); border:1px solid var(--band-line); border-radius:2px; transition:background .2s }
.mj-tool:hover{ background:rgba(255,255,255,.1); border-color:var(--band-k) }
.mj-tool__k{ font-family:var(--font-meta); font-weight:700; font-size:.62rem; letter-spacing:1.3px;
  text-transform:uppercase; color:var(--band-k) }
.mj-tool__t{ font-family:var(--font-display); font-weight:700; font-size:1.12rem; line-height:1.25 }
.mj-tool__d{ font-family:var(--font-body); font-size:.92rem; line-height:1.5; color:var(--band-muted) }
.mj-tool__go{ margin-top:6px; font-family:var(--font-meta); font-size:.76rem; font-weight:700 }
.mj-tool__go::after{ content:" ›" }

/* SMART GUIDES — warm paper and a shelf of covers. Covers are PLACEHOLDERS (Lucky: do
   not design them yet): a dark board with the brand line and the title set in type. */
.mj-band[data-band="guides"]{ --band:#F3EEE5; --band-ink:#0A0A0A; --band-k:#C8161C; --band-muted:#55524c; --band-line:rgba(0,0,0,.12) }
.mj-band[data-band="guides"] .mj-band__body{ display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:var(--sp-4) }
.mj-cover{ display:flex; flex-direction:column; gap:10px; text-decoration:none; color:var(--band-ink) }
.mj-cover__art{ position:relative; display:flex; flex-direction:column; justify-content:space-between; gap:12px;
  aspect-ratio:3/4; padding:16px 14px; color:#fff; border-radius:2px;
  background:linear-gradient(165deg,#1f1f24 0%,#0A0A0A 70%); box-shadow:0 10px 24px -14px rgba(0,0,0,.55);
  transition:transform .25s }
.mj-cover:hover .mj-cover__art{ transform:translateY(-4px) }
.mj-cover__art::before{ content:""; position:absolute; left:0; top:0; right:0; height:4px; background:var(--mj-red) }
.mj-cover__brand{ font-family:var(--font-meta); font-size:.56rem; letter-spacing:1.2px; text-transform:uppercase; color:rgba(255,255,255,.7) }
.mj-cover__title{ font-family:var(--font-display); font-weight:800; font-size:1rem; line-height:1.18 }
.mj-cover__desk{ font-family:var(--font-meta); font-size:.58rem; letter-spacing:1px; text-transform:uppercase; color:rgba(255,255,255,.55) }
/* A REAL COVER (when the guide has one) fills the board and the placeholder's type steps
   aside; the image's alt text carries the title. */
.mj-cover__img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; border-radius:2px; z-index:1 }
.mj-cover__art:has(.mj-cover__img)::before{ display:none }
.mj-cover__art:has(.mj-cover__img) > span{ visibility:hidden }
.mj-cover__state{ font-family:var(--font-meta); font-size:.7rem; font-weight:700; letter-spacing:.8px;
  text-transform:uppercase; color:var(--band-muted) }

/* THE NEWSLETTER — brand red; the button in black. */
.mj-band[data-band="news"]{ --band:#EC2227; --band-ink:#fff; --band-k:rgba(255,255,255,.85); --band-muted:rgba(255,255,255,.88) }
.mj-band[data-band="news"] .mj-band__t{ font-size:clamp(1.5rem, 2.4vw, 2.1rem) }
.mj-band__cta{ display:block }
.mj-band[data-band="news"] .mj-band__d{ margin-top:0; max-width:52ch; font-size:1.05rem }

body.dark-mode .mj-band[data-band="deals"]{ --band:#17171b }
body.dark-mode .mj-band[data-band="guides"]{ --band:#1d1b17; --band-ink:#f2efe9; --band-k:#ff6b6f; --band-muted:#b9b4aa }
body.dark-mode .mj-band[data-band="guides"] .mj-cover__art{ background:linear-gradient(165deg,#2b2b31 0%,#141417 70%) }

@media(max-width:999px){
  .mj-band__in{ grid-template-columns:1fr; gap:var(--sp-5) }
  .mj-band[data-band="guides"] .mj-band__body{ grid-template-columns:repeat(5,minmax(150px,1fr)); overflow-x:auto;
    scroll-snap-type:x mandatory; padding-bottom:8px }
  .mj-cover{ scroll-snap-align:start }
}
@media(max-width:599px){
  .mj-band{ padding:40px 0; margin-bottom:var(--sp-7) }
  .mj-band[data-band="tools"] .mj-band__body{ grid-template-columns:1fr }
  .mj-band[data-band="guides"] .mj-band__body{ grid-template-columns:repeat(5,minmax(140px,46%)) }
}
.mj-dp__adv--grow .mj-dp__advk{ color:var(--mj-ink) }
.mj-dp__disc{ color:#a1a1aa; font-size:.8rem }
.mj-dp__fitv{ display:block }
/* four plans sit two by two, never three and an orphan; a price with no offer takes the row */
.mj-dp__plans:has(> :nth-child(4):last-child){ grid-template-columns:repeat(2,minmax(0,1fr)) }
.mj-dp__top > .mj-pb__pay:only-child{ grid-column:1 / -1 }
body.dark-mode .mj-dp__adv--all{ color:#b4b4bc }
body.dark-mode .mj-dp__kk{ color:#8fb4ff }
body.dark-mode .mj-dp__t, body.dark-mode .mj-dp__adv--grow .mj-dp__advk{ color:#f4f4f5 }
body.dark-mode .mj-dp__gk{ color:#f4f4f5 }
@media (max-width:720px){ .mj-dp__plans:has(> :nth-child(4):last-child){ grid-template-columns:1fr } }
/* hover reads as "selecting": lift, deeper shadow, a tint of the brand; the pick keeps its frame */
.mj-dp__plan:hover, .mj-dp__plan[data-pick="1"]:hover{ box-shadow:0 14px 30px rgba(20,20,22,.16);
  background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 4%, #fff) }
body.dark-mode .mj-dp__plan:hover{ background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 8%, #1b1b1f) }
.mj-dp__plan:hover .mj-dp__go{ color:var(--brand-accent-text, var(--mj-ink)); text-decoration:underline; text-underline-offset:3px }

/* ── DEAL PANEL, REVISED (Lucky, 2026-10-01, second pass) ──────────────────────────────
   Our recommendations come first under a heading of their own, written for this article; the
   plans sit behind a toggle, introduced as a mirror of the vendor's pricing page, with a
   screenshot of that page kept as our evidence. */
.mj-dp__advice{ margin-top:18px }
.mj-dp__rech{ margin:0 0 4px; font-family:var(--font-display); font-weight:800; font-size:1.08rem; color:var(--mj-ink) }
body.dark-mode .mj-dp__rech{ color:#f4f4f5 }
.mj-dp__adv--also .mj-dp__advk{ color:var(--mj-ink) }
body.dark-mode .mj-dp__adv--also .mj-dp__advk{ color:#f4f4f5 }
.mj-dp__more{ margin:14px 0 0; background:#fff; border:1px solid var(--mj-rule); border-radius:var(--radius-md) }
body.dark-mode .mj-dp__more{ background:#1b1b1f; border-color:#2c2c33 }
.mj-dp__sum{ list-style:none; cursor:pointer; display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:14px 18px }
.mj-dp__sum::-webkit-details-marker{ display:none }
.mj-dp__sum:hover .mj-dp__t{ text-decoration:underline; text-underline-offset:3px }
.mj-dp__sumtxt{ display:flex; flex-direction:column; gap:2px; min-width:0 }
.mj-dp__more .mj-dp__t{ font-size:1rem }
.mj-dp__more .mj-dp__s{ margin:0 }
.mj-dp__cue{ flex:none; width:34px; height:34px; border-radius:50%; background:var(--mj-ink); position:relative }
.mj-dp__cue::before{ content:""; position:absolute; left:12px; top:10px; width:8px; height:8px;
  border-right:2.5px solid #fff; border-bottom:2.5px solid #fff; transform:rotate(45deg); transition:transform .15s ease }
.mj-dp__more[open] .mj-dp__cue::before{ top:14px; transform:rotate(-135deg) }
.mj-dp__more[open] > .mj-dp__sum{ border-bottom:1px solid var(--mj-rule) }
.mj-dp__more > .mj-dp__mirror, .mj-dp__more > .mj-dp__plans, .mj-dp__more > .mj-dp__note, .mj-dp__more > .mj-dp__shot{
  margin-left:18px; margin-right:18px }
.mj-dp__mirror{ margin-top:14px; margin-bottom:14px; padding:10px 12px; border-left:3px solid var(--brand-accent, var(--mj-growth));
  background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 6%, #fff); font-size:.86rem; line-height:1.5 }
body.dark-mode .mj-dp__mirror{ background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 10%, #1b1b1f) }
.mj-dp__mirrork{ display:block; font-family:var(--font-display); font-weight:800; font-size:.7rem; letter-spacing:.06em; text-transform:uppercase }
.mj-dp__mirrorv{ display:block }
.mj-dp__shot{ margin-top:14px; margin-bottom:18px }
.mj-dp__shot img{ display:block; width:100%; height:auto; border:1px solid var(--mj-rule); border-radius:var(--radius-sm) }
.mj-dp__shotcap{ margin-top:6px; font-family:var(--font-meta); font-size:.66rem; color:var(--mj-gray-500, #6b6b72) }
.mj-dp__more > .mj-dp__note:last-child{ padding-bottom:16px }
@media (max-width:720px){
  .mj-dp__more > .mj-dp__mirror, .mj-dp__more > .mj-dp__plans, .mj-dp__more > .mj-dp__note, .mj-dp__more > .mj-dp__shot{
    margin-left:12px; margin-right:12px }
  .mj-dp__sum{ padding:12px 14px }
}
/* the screenshot is evidence, not decoration: a thumbnail beside its caption */
.mj-dp__shot{ display:grid; grid-template-columns:220px minmax(0,1fr); gap:14px; align-items:center }
.mj-dp__shotimg{ max-width:220px }
.mj-dp__shot .mj-dp__shotcap{ margin:0; font-size:.72rem; line-height:1.5 }
@media (max-width:720px){ .mj-dp__shot{ grid-template-columns:1fr } .mj-dp__shotimg{ max-width:100% } }

/* ── WHAT IT WORKS WITH (2026-10-02, POS vertical) ─────────────────────────
   One element, two weights. `data-variant="full"` is the map: grouped rows,
   each item's state and note on its own line. `data-variant="compact"` is what
   a ranked product box carries: the names as chips, the notes dropped, so a hub
   can state the fact without becoming the integrations article (R6).
   A state is never a claim: "Connected" is the vendor's own list, "Coming soon"
   is the vendor's own words, and a group gated behind a paid plan says so. */
.mj-ww{ display:grid; gap:10px; margin:0; padding:16px 0 0; border-top:1px solid var(--mj-rule) }
body.dark-mode .mj-ww{ border-top-color:#2a2a2d }
.mj-ww__k{ margin:0; display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 10px;
  font-family:var(--font-display); font-weight:700; font-size:.95rem; line-height:1.2; color:var(--mj-trust) }
.mj-ww__k1{ font-weight:700 }
.mj-ww__k2{ font-weight:600; font-size:.8rem; color:var(--mj-text-muted) }
.mj-ww__groups{ list-style:none; margin:0; padding:0; display:grid; gap:0 }
.mj-ww__group{ display:grid; gap:6px; padding:12px 0; border-top:1px solid var(--mj-rule) }
.mj-ww__group:first-child{ border-top:0 }
body.dark-mode .mj-ww__group{ border-top-color:#2a2a2d }
.mj-ww__head{ display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 10px }
.mj-ww__kind{ margin:0; font-family:var(--font-display); font-weight:800; font-size:.8rem;
  letter-spacing:.3px; text-transform:uppercase; color:var(--mj-black) }
body.dark-mode .mj-ww__kind{ color:#fff }
.mj-ww__need{ margin:0; font-family:var(--font-meta); font-size:.62rem; font-weight:700; letter-spacing:.2px;
  text-transform:none; padding:2px 8px; border-radius:999px;
  color:var(--mj-trust); background:var(--mj-trust-subtle); border:1px solid rgba(37,99,235,.28) }
.mj-ww__need[data-tone="paid"]{ color:#8a5a00; background:#fff4dc; border-color:#f1d49a }
body.dark-mode .mj-ww__need{ color:#8fb4ff; background:rgba(37,99,235,.16); border-color:rgba(37,99,235,.34) }
body.dark-mode .mj-ww__need[data-tone="paid"]{ color:#f3c56b; background:rgba(243,197,107,.1); border-color:rgba(243,197,107,.3) }
.mj-ww__items{ list-style:none; margin:0; padding:0; display:grid; gap:4px }
.mj-ww__item{ display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 8px; min-width:0 }
.mj-ww__name{ font-family:var(--font-display); font-weight:700; font-size:.9rem; color:var(--mj-black) }
body.dark-mode .mj-ww__name{ color:#fff }
.mj-ww__state{ font-family:var(--font-meta); font-size:.58rem; font-weight:700; letter-spacing:.3px;
  padding:2px 7px; border-radius:999px; color:var(--mj-green-ink,#136c3a);
  background:var(--mj-green-subtle,#e7f6ed); border:1px solid rgba(19,108,58,.22) }
.mj-ww__item[data-state="coming_soon"] .mj-ww__state{ color:#8a5a00; background:#fff4dc; border-color:#f1d49a }
body.dark-mode .mj-ww__state{ color:#7fd6a2; background:rgba(19,108,58,.18); border-color:rgba(19,108,58,.4) }
body.dark-mode .mj-ww__item[data-state="coming_soon"] .mj-ww__state{ color:#f3c56b; background:rgba(243,197,107,.1); border-color:rgba(243,197,107,.3) }
.mj-ww__note{ font-family:var(--font-meta); font-size:.66rem; line-height:1.45; letter-spacing:.2px; color:var(--mj-text-muted) }
body.dark-mode .mj-ww__note{ color:#a8a8ae }
.mj-ww__foot{ margin:0; font-family:var(--font-meta); font-size:.62rem; line-height:1.5;
  letter-spacing:.2px; color:var(--mj-text-muted) }
body.dark-mode .mj-ww__foot{ color:#a8a8ae }
/* COMPACT: the names, and nothing that belongs to the integrations article. */
.mj-ww[data-variant="compact"]{ padding-top:12px; gap:6px }
.mj-ww[data-variant="compact"] .mj-ww__group{ padding:6px 0; border-top:0; grid-template-columns:none }
.mj-ww[data-variant="compact"] .mj-ww__kind{ font-size:.7rem }
.mj-ww[data-variant="compact"] .mj-ww__items{ display:flex; flex-wrap:wrap; gap:6px }
.mj-ww[data-variant="compact"] .mj-ww__item{ gap:5px; padding:3px 9px; border-radius:999px;
  background:var(--mj-surface-2,#f4f4f6); border:1px solid var(--mj-rule) }
body.dark-mode .mj-ww[data-variant="compact"] .mj-ww__item{ background:#201f23; border-color:#2a2a2d }
.mj-ww[data-variant="compact"] .mj-ww__name{ font-size:.78rem }
.mj-ww[data-variant="compact"] .mj-ww__note{ display:none }
.mj-ww[data-variant="compact"] .mj-ww__item[data-state="live"] .mj-ww__state{ display:none }
.mj-ww[data-variant="compact"] .mj-ww__state{ font-size:.54rem; padding:1px 6px }
.mj-pb--rank > .mj-pb__works{ grid-column:1/-1 }
@media (max-width:560px){
  .mj-ww__item{ gap:3px 7px }
  .mj-ww__note{ flex-basis:100%; }
}

/* MOST DISCUSSED WITH FEWER THAN FOUR (Phase 6): the last recency section on Just In takes
   whatever stories are left. Two or three cards share the row instead of leaving a hole. */
.mj-nx-disc__cards:has(> :nth-child(3):last-child){ grid-template-columns:repeat(3,minmax(0,1fr)) }
.mj-nx-disc__cards:has(> :nth-child(2):last-child){ grid-template-columns:repeat(2,minmax(0,1fr)) }
@media(max-width:599px){ .mj-nx-disc__cards:has(> :nth-child(3):last-child),
  .mj-nx-disc__cards:has(> :nth-child(2):last-child){ grid-template-columns:1fr } }

/* ── FIRST MENTION: THE NUMBER AND THE FACE (Lucky, Part 2 · 2.7, J-3) ─────────────────
   A table whose rows are the article's ranked companies shows each one's rank IN THE ARTICLE
   and its logo beside the name, the first time the reader meets them. All rows or none. */
.mj-tw__who{ display:flex; align-items:center; gap:10px; min-width:0 }
.mj-tw__rk{ flex:none; display:inline-flex; align-items:center; justify-content:center; width:22px; height:22px;
  border-radius:4px; background:var(--mj-ink); color:#fff; font:800 .72rem/1 var(--font-display) }
tr.is-win .mj-tw__rk{ background:var(--mj-red) }
.mj-tw__lg{ flex:none; display:inline-flex; align-items:center; justify-content:center; width:64px; height:30px;
  padding:3px 6px; background:#fff; border:1px solid var(--mj-rule); border-radius:5px; box-sizing:border-box }
.mj-tw__lg[data-kind="logo-light"]{ background:#141416; border-color:#141416 }
.mj-tw__lgimg{ display:block; max-width:100%; max-height:22px; width:auto; height:auto; object-fit:contain }
.mj-tw__nm{ min-width:0 }
body.dark-mode .mj-tw__lg{ border-color:#2c2c33 }
@media (max-width:720px){
  .mj-tw__who{ gap:7px }
  .mj-tw__lg{ width:48px; height:24px; padding:2px 4px }
  .mj-tw__lgimg{ max-height:17px }
}

/* FEE SCALE: EVERY PROVIDER WITH ITS FACE (Lucky, Part 2 · 2.7.1, J-3) */
.mj-fscale__lg{ display:inline-flex; align-items:center; justify-content:center; vertical-align:middle;
  width:46px; height:24px; margin-right:8px; padding:2px 5px; box-sizing:border-box;
  background:#fff; border:1px solid var(--mj-rule); border-radius:5px }
.mj-fscale__lg[data-kind="logo-light"]{ background:#141416; border-color:#141416 }
.mj-fscale__lgimg{ display:block; max-width:100%; max-height:18px; width:auto; height:auto; object-fit:contain }
body.dark-mode .mj-fscale__lg{ border-color:#2c2c33 }
.mj-fscale__nm{ }

/* FINDER: THE FACE ABOVE THE NAME (Lucky, Part 2 · 2.7.2.1, J-3). A white chip reads on the
   black standing band, the red answered band and any colour; only the lead's face shows. */
.mj-finder__faces{ margin:6px 0 10px; line-height:0 }
.mj-finder__face{ display:none; align-items:center; justify-content:center; height:64px; min-width:64px; padding:6px 10px;
  box-sizing:border-box; background:#fff; border-radius:12px; box-shadow:0 4px 14px rgba(0,0,0,.25) }
.mj-finder__face[data-role="lead"][data-shown="yes"]{ display:inline-flex }
.mj-finder__face[data-kind="logo"]{ height:48px; min-width:120px; padding:8px 14px }
.mj-finder__face[data-kind="logo-light"]{ height:48px; min-width:120px; padding:8px 14px; background:#141416;
  box-shadow:0 0 0 1px rgba(255,255,255,.25) }
.mj-finder__faceimg{ display:block; max-height:100%; max-width:150px; width:auto; height:auto; object-fit:contain }
/* BIZFIT: the product to start with, shown above its name (2.7.2.1) */
.mj-bizfit__face{ display:flex; align-items:center; justify-content:center; width:72px; height:72px; margin:4px 0 8px;
  padding:6px; box-sizing:border-box; background:#fff; border:1px solid var(--mj-rule); border-radius:10px }
.mj-bizfit__face[data-kind="logo"]{ width:120px; height:44px }
.mj-bizfit__face[data-kind="logo-light"]{ width:120px; height:44px; background:#141416; border-color:#141416 }
.mj-bizfit__faceimg{ display:block; max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain }
.mj-bizfit__val{ display:block }

/* THE QUAD — an archive's opening (Phase 7, Lucky 2026-09-20: the homepage's old four-story
   hero "becomes the main section for some archives"). Newsroom tiles, so it carries pictures:
   the pinned story big, one tall beside it, two stacked. A fixed band of height on desktop so
   the four weights read at a glance; tiles fill their cells instead of keeping their ratios. */
.mj-nx-quad__g{ display:grid; grid-template-columns:minmax(0,2fr) minmax(0,1fr) minmax(0,1fr);
  grid-template-rows:repeat(2,minmax(0,1fr)); gap:var(--sp-4); height:clamp(420px, 38vw, 540px) }
.mj-nx-quad__g > .mj-nx-tile{ aspect-ratio:auto; height:100% }
.mj-nx-quad__g > :nth-child(1){ grid-column:1; grid-row:1 / span 2 }
.mj-nx-quad__g > :nth-child(2){ grid-column:2; grid-row:1 / span 2 }
@media(max-width:899px){
  .mj-nx-quad__g{ grid-template-columns:repeat(2,minmax(0,1fr)); grid-template-rows:none; height:auto }
  .mj-nx-quad__g > .mj-nx-tile{ height:auto; aspect-ratio:4/3 }
  .mj-nx-quad__g > :nth-child(1){ grid-column:1 / -1; grid-row:auto; aspect-ratio:16/9 }
  .mj-nx-quad__g > :nth-child(2){ grid-column:1; grid-row:span 2; aspect-ratio:auto }
}
@media(max-width:599px){
  .mj-nx-quad__g{ grid-template-columns:1fr }
  .mj-nx-quad__g > .mj-nx-tile, .mj-nx-quad__g > :nth-child(2){ grid-column:1; grid-row:auto; aspect-ratio:16/10 }
}

/* TAB LABELS STAY ON ONE LINE (Phase 7): four tabs ("All 16" was the fourth) in a 280px rail
   wrapped the last one onto two lines. Labels never wrap; the row scrolls inside itself. */
.mj-nx-tabs{ overflow-x:auto; scrollbar-width:none }
.mj-nx-tabs::-webkit-scrollbar{ display:none }
.mj-nx-tabs__b{ white-space:nowrap; flex:0 0 auto }

/* SMART RECOMMENDATION: the pick shown above its name, and an option that answers the pointer
   (Lucky, Part 2 · 2.7.2, J-3) */
.mj-rec__face{ display:flex; align-items:center; justify-content:center; width:84px; height:84px; margin:10px 0 8px;
  padding:6px; box-sizing:border-box; background:#fff; border:1px solid var(--mj-rule); border-radius:12px;
  transition:transform .16s ease, box-shadow .16s ease }
.mj-rec__face[data-kind="logo"]{ width:140px; height:52px }
.mj-rec__face[data-kind="logo-light"]{ width:140px; height:52px; background:#141416; border-color:#141416 }
.mj-rec__faceimg{ display:block; max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain }
.mj-rec__opt:hover .mj-rec__face{ transform:translateY(-2px) scale(1.03); box-shadow:0 8px 18px rgba(20,20,22,.12) }
.mj-rec__opt:hover .mj-rec__cta{ text-decoration:underline; text-underline-offset:3px }
body.dark-mode .mj-rec__face{ border-color:#2c2c33 }
/* ONE PICK FILLS THE BLOCK: its face on the left, the case beside it (J-3) */
.mj-rec__seg:has(> .mj-rec__opt:only-child){ grid-template-columns:1fr }
.mj-rec__seg > .mj-rec__opt:only-child:has(> .mj-rec__face){ display:grid; grid-template-columns:132px minmax(0,1fr);
  column-gap:24px; align-content:start }
.mj-rec__seg > .mj-rec__opt:only-child:has(> .mj-rec__face) > *{ grid-column:2 }
.mj-rec__seg > .mj-rec__opt:only-child > .mj-rec__face{ grid-column:1; grid-row:1 / span 7; width:132px; height:132px; margin:4px 0 0 }
.mj-rec__seg > .mj-rec__opt:only-child > .mj-rec__face[data-kind^="logo"]{ height:64px }
.mj-rec__seg > .mj-rec__opt:only-child > .mj-rec__cta{ justify-self:start }
@media (max-width:620px){
  .mj-rec__seg > .mj-rec__opt:only-child:has(> .mj-rec__face){ grid-template-columns:1fr }
  .mj-rec__seg > .mj-rec__opt:only-child:has(> .mj-rec__face) > *, .mj-rec__seg > .mj-rec__opt:only-child > .mj-rec__face{ grid-column:1; grid-row:auto }
  .mj-rec__seg > .mj-rec__opt:only-child > .mj-rec__face{ width:96px; height:96px }
}

/* ── "IN THIS GUIDE / IN THIS LIST", REDESIGNED (Lucky, Part 2 · 2.8, J-3) ───────────────
   From the list prototype's "tap to jump" rank rail: a white card, a display-face title with a
   small tag, squared numerals, rows that answer the pointer, and a scroll-spy that shows where
   the reader is (current = red numeral, passed = ticked). */
.mj-toc:not(.mj-toc--rail){ background:#FDFDFD; border:1px solid var(--mj-rule); border-radius:var(--radius-md);
  padding:18px 20px 8px; box-shadow:0 2px 10px rgba(20,20,22,.04) }
body.dark-mode .mj-toc:not(.mj-toc--rail){ background:#17171b; border-color:#2c2c33 }
.mj-toc .mj-toc__h{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; margin:0 0 8px;
  font-family:var(--font-display); font-weight:800; font-size:1.05rem; letter-spacing:0; text-transform:none; color:var(--mj-trust) }
.mj-toc .mj-toc__h::after{ content:none }
body.dark-mode .mj-toc .mj-toc__h{ color:#8fb4ff }
.mj-toc__tap{ font-family:var(--font-meta); font-weight:700; font-size:.62rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--mj-gray-500, #6b6b72) }
.mj-toc:not(.mj-toc--rail) li{ border-top:1px solid var(--mj-rule) }
.mj-toc:not(.mj-toc--rail) li:first-child{ border-top:0 }
.mj-toc:not(.mj-toc--rail) a{ display:grid; grid-template-columns:26px minmax(0,1fr) 18px; align-items:center; gap:12px;
  margin:0 -10px; padding:10px; border-radius:var(--radius-sm); color:var(--mj-ink); text-decoration:none;
  transition:background .14s ease }
.mj-toc:not(.mj-toc--rail) a::before{ content:none }
.mj-toc:not(.mj-toc--rail) a::after{ content:"\2192"; justify-self:end; opacity:0; transform:translateX(-4px);
  transition:opacity .14s ease, transform .14s ease; color:var(--mj-trust) }
.mj-toc__n{ display:inline-flex; align-items:center; justify-content:center; width:26px; height:26px; border-radius:5px;
  background:color-mix(in srgb, var(--mj-trust) 10%, #fff); color:var(--mj-trust); font:800 .78rem/1 var(--font-display);
  transition:background .14s ease, color .14s ease }
.mj-toc__n::before{ content:counter(mjtoc) }
/* J-7b (Lucky, 2026-10-04): "make the body text be normal body text … the title … the numbering and highlights
   mashjoy.com blue instead of black … the same background as other inserts: #FDFDFD", so the section reads
   "a little bit less strong but useful", in the house style. */
.mj-toc__l{ font-family:var(--font-body); font-weight:400; font-size:1rem; line-height:1.4 }
.mj-toc:not(.mj-toc--rail) a:hover{ background:var(--mj-surface-2, #f6f6f7) }
.mj-toc:not(.mj-toc--rail) a:hover .mj-toc__n{ background:var(--mj-trust); color:#fff }
.mj-toc:not(.mj-toc--rail) a:hover::after{ opacity:1; transform:translateX(0) }
.mj-toc li.is-current .mj-toc__n{ background:var(--mj-trust); color:#fff }
.mj-toc li.is-current .mj-toc__l{ color:var(--mj-ink) }
.mj-toc li.is-past .mj-toc__n{ background:var(--mj-growth); color:#fff }
.mj-toc li.is-past .mj-toc__n::before{ content:"\2713" }
.mj-toc li.is-past .mj-toc__l{ color:var(--mj-gray-500, #6b6b72) }
body.dark-mode .mj-toc:not(.mj-toc--rail) a{ color:#ececf0 }
body.dark-mode .mj-toc:not(.mj-toc--rail) a:hover{ background:#222228 }
body.dark-mode .mj-toc__n{ background:#1d2a45; color:#8fb4ff }
.mj-toc__t{ min-width:0 }
/* a row with a face lets its name wrap beside the logo, so the face never widens the table (J-3) */
.mj-tw td.mj-tw__rowlabel:has(.mj-tw__lg), .mj-tw td.mj-tw__rowlabel:has(.mj-tw__rk){ white-space:normal; width:auto; min-width:150px; max-width:230px }
.mj-tw__lg{ width:56px }

/* ── SLIM ROW WITH A QUICK REVIEW (Lucky, Part 2 · 2.3, J-4) ──────────────────────────────
   For a product the reader meets for the first time: price on top, the buy button, then a Quick
   review toggle; the tray carries the review we'd write, its packages and the honest bundle saving. */
.mj-pb.mj-pb--slim:has(.mj-pb__qr) .mj-pb__right{ flex-direction:column; align-items:stretch; gap:8px; min-width:172px }
.mj-pb.mj-pb--slim:has(.mj-pb__qr) .mj-pb__price{ text-align:right }
.mj-pb.mj-pb--slim:has(.mj-pb__qr) .mj-pb__cta{ text-align:center }
a.mj-ireveal__t.mj-pb__qr{ display:flex; align-items:center; justify-content:center; gap:6px; padding:10px 14px;
  border:1.5px solid var(--mj-ink); border-radius:var(--radius-sm); background:#fff; color:var(--mj-ink);
  font-family:var(--font-display); font-weight:800; font-size:.74rem; letter-spacing:.06em; text-transform:uppercase;
  text-decoration:none; border-bottom-style:solid }
a.mj-ireveal__t.mj-pb__qr:hover{ background:var(--mj-ink); color:#fff }
a.mj-ireveal__t.mj-pb__qr[aria-expanded="true"]{ background:var(--mj-ink); color:#fff }
body.dark-mode a.mj-ireveal__t.mj-pb__qr{ background:transparent; color:#f4f4f5; border-color:#f4f4f5 }
.mj-qr{ grid-column:1 / -1; margin:10px -20px -16px; padding:24px 26px 22px; border-top:1px solid var(--mj-rule);
  background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 5%, #fff) }
.mj-qr[hidden]{ display:none }
.mj-pb--slim + .mj-pb--slim, .mj-pb--slim + * > .mj-pb--slim:first-child{ margin-top:var(--sp-5) }
body.dark-mode .mj-qr{ background:color-mix(in srgb, var(--brand-accent, var(--mj-growth)) 8%, #141416); border-color:#2c2c33 }
.mj-pb .mj-qr .mj-qr__k{ margin:0 0 6px; font-family:var(--font-meta); font-weight:700; font-size:.66rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--mj-gray-500, #6b6b72) }
/* J-7b (Lucky, 2026-10-04, on Yoco vs iKhokha): "dont make it bold keep it as body font with strategic bolding … the
   shorter the section the better … add proper spacing … colour code headings". */
.mj-pb .mj-qr .mj-qr__verdict{ margin:0 0 20px; font-family:var(--font-body); font-weight:400; font-size:1rem; line-height:1.65; color:var(--mj-ink) }
.mj-pb .mj-qr .mj-qr__verdict b{ font-weight:700 }
body.dark-mode .mj-pb .mj-qr .mj-qr__verdict{ color:#f4f4f5 }
.mj-qr__cols{ display:grid; grid-template-columns:1fr 1fr; gap:14px 22px; margin-bottom:12px }
.mj-pb .mj-qr .mj-qr__h{ margin:10px 0 6px; font-family:var(--font-display); font-weight:800; font-size:.72rem; letter-spacing:.06em;
  text-transform:uppercase; color:var(--mj-ink) }
.mj-pb .mj-qr .mj-qr__col > .mj-qr__h:first-child{ margin-top:0 }
body.dark-mode .mj-pb .mj-qr .mj-qr__h{ color:#f4f4f5 }
.mj-qr__list{ list-style:none; margin:0; padding:0; display:grid; gap:6px }
.mj-qr__li{ position:relative; padding-left:22px; font-size:.9rem; line-height:1.45 }
.mj-qr__li::before{ content:""; position:absolute; left:0; top:4px; width:14px; height:14px; border-radius:50% }
.mj-qr__list--good .mj-qr__li::before{ background:var(--mj-growth) }
.mj-qr__list--for .mj-qr__li::before{ background:var(--mj-trust) }
.mj-qr__list--skip .mj-qr__li::before{ background:color-mix(in srgb, var(--mj-alert) 55%, #fff) }
.mj-qr__list--skip .mj-qr__li{ color:var(--mj-gray-600, #55555b) }
.mj-pb .mj-qr .mj-qr__vs{ margin:4px 0 12px; padding:10px 12px; background:#fff; border:1px solid var(--mj-rule); border-radius:var(--radius-sm);
  font-size:.9rem; line-height:1.5 }
body.dark-mode .mj-pb .mj-qr .mj-qr__vs{ background:#1b1b1f; border-color:#2c2c33 }
.mj-qr__vsk{ font-family:var(--font-display); font-weight:800; margin-right:4px }
.mj-pb .mj-qr .mj-qr__h--good{ color:var(--mj-growth-text, #047857) }
.mj-pb .mj-qr .mj-qr__h--for{ color:var(--mj-trust) }
.mj-pb .mj-qr .mj-qr__h--skip{ color:var(--mj-alert) }
body.dark-mode .mj-pb .mj-qr .mj-qr__h--good{ color:#34d8a6 }
body.dark-mode .mj-pb .mj-qr .mj-qr__h--for{ color:#8fb4ff }
body.dark-mode .mj-pb .mj-qr .mj-qr__h--skip{ color:#ff8b93 }
/* THE PACKAGES IN ONE ROW: a swipeable strip with a peek of the next card, and arrows for a mouse. */
.mj-qr__pkhead{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin:14px 0 8px }
.mj-pb .mj-qr .mj-qr__pkhead .mj-qr__h{ margin:0 }
.mj-qr__nav{ display:flex; gap:6px; flex:none }
.mj-qr__prev, .mj-qr__next{ width:30px; height:30px; border-radius:50%; border:1px solid var(--mj-rule); background:#fff; cursor:pointer;
  position:relative; transition:border-color .15s ease, opacity .15s ease }
.mj-qr__prev::before, .mj-qr__next::before{ content:""; position:absolute; left:50%; top:50%; width:7px; height:7px;
  border-top:2px solid var(--mj-black); border-right:2px solid var(--mj-black) }
.mj-qr__next::before{ transform:translate(-65%,-50%) rotate(45deg) }
.mj-qr__prev::before{ transform:translate(-35%,-50%) rotate(-135deg) }
.mj-qr__prev:hover:not(:disabled), .mj-qr__next:hover:not(:disabled){ border-color:var(--mj-ink) }
.mj-qr__prev:disabled, .mj-qr__next:disabled{ opacity:.35; cursor:default }
.mj-qr__prev:focus-visible, .mj-qr__next:focus-visible{ outline:2px solid var(--mj-trust); outline-offset:2px }
body.dark-mode .mj-qr__prev, body.dark-mode .mj-qr__next{ background:#1b1b1f; border-color:#3a3a40 }
body.dark-mode .mj-qr__prev::before, body.dark-mode .mj-qr__next::before{ border-color:#fff }
.mj-qr__pks{ list-style:none; margin:0; padding:2px 2px 10px; display:grid; grid-auto-flow:column;
  grid-auto-columns:calc((100% - 20px) / 3.2); gap:10px; overflow-x:auto; overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory; scrollbar-width:thin }
.mj-qr__pk{ scroll-snap-align:start }
.mj-qr__pk{ display:flex; flex-direction:column; gap:4px; padding:12px 14px; background:#fff; border:1px solid var(--mj-rule);
  border-top:3px solid var(--brand-accent, var(--mj-growth)); border-radius:var(--radius-sm) }
.mj-qr__pk[data-pick="1"]{ border-width:2px 2px 2px; border-top-width:3px; border-color:var(--brand-accent, var(--mj-growth)) }
body.dark-mode .mj-qr__pk{ background:#1b1b1f; border-color:#2c2c33 }
.mj-qr__pkn{ font-family:var(--font-display); font-weight:800; font-size:.95rem }
.mj-qr__pki{ font-size:.84rem; line-height:1.4; color:var(--mj-gray-600, #55555b) }
.mj-qr__pkp{ display:flex; align-items:baseline; gap:8px; margin-top:auto; padding-top:4px }
.mj-qr__pkv{ font-family:var(--font-display); font-weight:800; font-size:1.2rem }
.mj-qr__pkw{ font-size:.8rem; color:var(--mj-gray-400) }
.mj-qr__pksave{ font-family:var(--font-meta); font-size:.66rem; font-weight:700; letter-spacing:.04em; color:var(--mj-growth-text, #047857) }
.mj-qr__pksave[data-tone="none"]{ color:var(--mj-gray-500, #6b6b72) }
.mj-pb .mj-qr .mj-qr__note{ margin:10px 0 0; font-family:var(--font-meta); font-size:.64rem; line-height:1.5; color:var(--mj-gray-500, #6b6b72) }
@media (max-width:620px){
  .mj-pb.mj-pb--slim:has(.mj-pb__qr) .mj-pb__right{ min-width:0 }
  .mj-qr{ margin:6px -16px -16px; padding:16px }
  .mj-qr__cols{ grid-template-columns:1fr }
  .mj-qr__pks{ grid-auto-columns:78% }
  .mj-qr__nav{ display:none }
}
.mj-qr__in{ display:block }
.mj-qr__h--good, .mj-qr__h--for, .mj-qr__h--skip, .mj-qr__h--pks{ }

/* THE HEADER ON ONE LINE (Lucky, 2026-10-03). Measured from the pixels at 1500px: the icons and
   the Subscribe button centre on 69.5px, the menu labels' capitals on 68.5, and the wordmark's
   lower-case letters on 71.5. The logo's baseline sat 5px below the menu's, which is what the eye
   reads as "not aligned". The logo lifts 2px and the labels drop 1px, so all four share 69.5. */
.mj-bar .mj-logo{ position:relative; top:-2px }
.mj-nav > ul > li > .mm-top{ position:relative; top:1px }
.mj-qr__vsv{ display:inline }
