/* =====================================================================
   v2.css — the Option 3 theme.

   HOW THIS COEXISTS WITH global.css
   ---------------------------------------------------------------------
   Every rule here is scoped under `body.v2`. A page opts in by setting
   $themeV2 = true before including header.php; everything else keeps
   rendering exactly as it did.

   That scoping is not tidiness. Twice during this rebuild a new rule
   was written as a bare class - .portal-title, .page-hero - and lost
   silently to a global.css descendant selector like `.hero-text h1`,
   rendering at 60.8px while the rule asked for 41.6px. Scoping under
   body.v2 gives every rule here (0,2,x) specificity, which beats those
   without anyone having to think about it.

   CONTRAST
   ---------------------------------------------------------------------
   Nothing here falls below 5:1 on the page background. The greys are
   capped at --t3 (#b59bb5); the discarded Option 2 used #6e6379 on
   near-black, about 3.4:1, which is why it was hard to read.

   PLAIN LANGUAGE
   ---------------------------------------------------------------------
   Class names describe what a thing IS, not protocol jargon: .v2-stat,
   .v2-card, .v2-nav. "Dossier", "Citizen" and "Chamber" are being
   retired from the copy, so they are not going into the stylesheet
   either.
   ===================================================================== */

/* ── THE HUE AXIS ───────────────────────────────────────────────────
   Every purple below sits on hue 300, where red and blue are equal.

   That number is not a taste call, it is v1's. v1's body gradient ran
   through #570a57, which is rgb(87, 10, 87): exactly hue 300. The
   first cut of this theme drifted to hue 266 for the page background
   and 285 for the hero field, both of which carry more blue than red,
   and Ken spotted it as the wrong shade before anyone measured it.

   The shift only moved hue. Saturation and lightness are untouched,
   so the contrast ratios are the same to within a rounding error, and
   every one of them went UP rather than down:

     --t2  10.61:1 -> 10.76:1     --t4  5.35:1 -> 5.51:1
     --t3   7.60:1 ->  7.76:1     white  19.9:1 -> 19.6:1

   --accent (#db46e6, hue 296) and --accent-deep (#8b1fb0, hue 285)
   are deliberately NOT on the axis. Those two are lifted straight
   from v1's own --light-purple-grad, which ran #a224c7 to #db46e6 at
   hue 286 and 296. The bright brand purple was always slightly bluer
   than the field it sat on; that relationship is v1's and it stays.
   ------------------------------------------------------------------ */
body.v2 {
    /* Ground and surfaces */
    --bg:          #160616;
    --bg-raised:   #230b23;
    --surface:     rgba(255, 255, 255, 0.045);
    --surface-2:   rgba(255, 255, 255, 0.08);
    --line:        rgba(255, 255, 255, 0.11);
    --line-strong: rgba(255, 255, 255, 0.16);

    /* Text — every one of these clears 5:1 on --bg */
    --t1: #ffffff;
    --t2: #d2b8d2;
    --t3: #b59bb5;
    --t4: #9b7f9b;   /* 13px+ only, never body copy */

    /* Brand */
    --accent:      #db46e6;
    --accent-deep: #8b1fb0;
    --accent-soft: #f0a5f6;
    --grad:        linear-gradient(135deg, #db46e6, #8b1fb0);

    /* Meaning */
    --good:    #3ddc97;
    --warn:    #ffb340;
    --info:    #7c9cff;
    /* 7.06:1 on --bg. index.php was carrying this as a literal in two
       inline styles before /governance needed it in a dozen places. */
    --bad:     #ff6b6b;
    --good-bg: rgba(61, 220, 151, 0.16);
    --warn-bg: rgba(255, 179, 64, 0.09);
    --bad-bg:  rgba(255, 107, 107, 0.13);

    /* Rhythm */
    --pad:    56px;
    --radius: 22px;

    background: var(--bg);
    color: var(--t1);
    font-family: 'Quicksand', sans-serif;

    /* global.css sets `body { padding-top: 70px }` to clear the v1
       sticky header. On a v2 page that clearance is applied a second
       time by .v2-main, so the page sat 70px lower than intended and
       the hero could never reach the top of the window however far it
       was pulled up. .v2-main owns the clearance here. */
    padding-top: 0;
}

@media (max-width: 900px) {
    body.v2 { --pad: 16px; }
}

/* ── NAVBAR ─────────────────────────────────────────────────────────
   Translucent and blurred, the way the v1 header was. The blur is the
   point: it has to sit over the hero gradient without hiding it.
   ------------------------------------------------------------------ */
body.v2 .v2-nav {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 1000;
    height: 76px;
    box-sizing: border-box;
    padding: 0 var(--pad);
    display: flex;
    align-items: center;
    gap: 44px;
    background: rgba(22, 6, 22, 0.55);
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    border-bottom: 1px solid var(--line);
}
body.v2 .v2-nav-logo { display: flex; align-items: center; min-height: 44px; }
/* fedora1.png carries a real alpha channel now, so nothing needs a
   blend mode to hide a background that is no longer there. The blend
   was also wrong on the navbar: mix-blend-mode composites against the
   nearest stacking context, and .v2-nav creates one with
   backdrop-filter, so the logo blended against the bar's own dark
   fill rather than the page behind it. It looked right only where the
   page behind happened to be dark too. */
/* 34, not the wordmark's 30. A square mark reads smaller than a
   wordmark of the same height, because the eye measures a logo by the
   area it covers and the wordmark was four times wider. */
body.v2 .v2-nav-logo img { height: 34px; width: 34px; display: block; }
body.v2 .v2-nav-links { flex-grow: 1; display: flex; align-items: center; gap: 6px; }
body.v2 .v2-nav-link {
    padding: 9px 16px;
    border-radius: 10px;
    font-size: 15px;
    font-weight: 600;
    color: var(--t2);
    text-decoration: none;
    transition: background 0.18s ease, color 0.18s ease;
}
body.v2 .v2-nav-link:hover { color: var(--t1); background: rgba(255,255,255,0.06); }
body.v2 .v2-nav-link.is-current { color: var(--t1); background: var(--surface-2); }
body.v2 .v2-nav-actions { display: flex; align-items: center; gap: 12px; }

/* The fixed navbar needs the page pushed down by its own height. */
body.v2 .v2-main { padding-top: 76px; }

/* Shown at EVERY width, not just below 900px.
   The navbar has room for five links; the drawer is the whole site
   map. With the burger hidden on desktop, eleven pages were
   reachable on a phone and only from the footer on a laptop, which
   is the reachability rule failing in the other direction. */
body.v2 .v2-burger {
    display: flex;
    width: 44px; height: 44px;
    border: none; background: transparent;
    align-items: center; justify-content: center;
    cursor: pointer; padding: 0;
    border-radius: 12px;
    transition: background .15s ease;
}
body.v2 .v2-burger:hover { background: rgba(255,255,255,0.07); }

@media (max-width: 900px) {
    body.v2 .v2-nav { height: 64px; gap: 0; justify-content: space-between; }
    body.v2 .v2-nav-links { display: none; }
    /* 36 rather than 30. The bar is 64 here against 76 on the desktop,
       so the old value left the mark looking smaller on the narrow
       screen than on the wide one, which is backwards. */
    body.v2 .v2-nav-logo img { height: 36px; width: 36px; }
    body.v2 .v2-main { padding-top: 64px; }
    body.v2 .v2-signin { display: none; }
}

/* ── ACCOUNT MENU ───────────────────────────────────────────────────
   The avatar and what drops out of it. Shown at every width: signing
   out is exactly the thing a phone user needs and could not reach
   while this was missing, so the buy button shrinks to make room
   rather than the avatar being hidden.
   ------------------------------------------------------------------ */
body.v2 .v2-user { position: relative; flex-shrink: 0; }
body.v2 .v2-user-trigger {
    display: flex; align-items: center; justify-content: center;
    width: 44px; height: 44px;
    padding: 0;
    /* A rounded square, matching the 12px on the buttons beside it,
       not a circle. Nothing else in this theme is a circle except the
       6px status dot. */
    border-radius: 12px;
    border: 1px solid var(--line-strong);
    background: rgba(255,255,255,0.06);
    cursor: pointer;
    overflow: hidden;
    transition: border-color 0.18s ease;
}
body.v2 .v2-user-trigger img { width: 100%; height: 100%; object-fit: cover; display: block; }
body.v2 .v2-user-trigger:hover,
body.v2 .v2-user-trigger.is-open { border-color: var(--accent); }

body.v2 .v2-user-menu {
    position: absolute;
    top: calc(100% + 12px);
    right: 0;
    z-index: 1100;
    width: 244px;
    padding: 8px;
    border: 1px solid var(--line-strong);
    border-radius: 16px;
    background: var(--bg-raised);
    box-shadow: 0 20px 50px rgba(0,0,0,0.55);
    /* display:none rather than opacity, so a closed menu is not a set
       of focusable links sitting invisibly over the page. */
    display: none;
}
body.v2 .v2-user-menu.is-open { display: block; }

body.v2 .v2-user-head {
    display: flex; flex-direction: column; gap: 3px;
    padding: 10px 12px 12px;
    border-bottom: 1px solid var(--line);
    margin-bottom: 6px;
}
body.v2 .v2-user-head-k { font-size: 11.5px; font-weight: 700; letter-spacing: 0.7px; color: var(--t4); }
body.v2 .v2-user-head-v {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13.5px; color: var(--t1);
}

body.v2 .v2-user-menu a {
    display: flex; align-items: center; gap: 11px;
    box-sizing: border-box;
    min-height: 44px;
    padding: 11px 12px;
    border-radius: 10px;
    font-size: 14.5px; font-weight: 600;
    color: var(--t2);
    text-decoration: none;
}
body.v2 .v2-user-menu a:hover { background: rgba(255,255,255,0.07); color: #fff; }
body.v2 .v2-user-menu a svg { flex-shrink: 0; }
body.v2 .v2-user-menu a.is-out { color: var(--bad); }
body.v2 .v2-user-menu a.is-out:hover { background: var(--bad-bg); color: var(--bad); }
body.v2 .v2-user-rule { height: 1px; background: var(--line); margin: 6px 4px; }

@media (max-width: 900px) {
    /* 244px of menu would hang off a 375px screen anchored to the
       right of a 44px avatar. Anchored to the viewport instead. */
    body.v2 .v2-user-menu { width: auto; right: 0; min-width: 232px; }
}

/* ── BUTTONS ────────────────────────────────────────────────────────
   One gradient on the page, on the one thing you want pressed.
   44px minimum height everywhere: these are touch targets.
   ------------------------------------------------------------------ */
body.v2 .v2-btn,
body.v2 .v2-btn-ghost,
body.v2 .v2-btn-light {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 11px 24px;
    border-radius: 12px;
    font-family: 'Quicksand', sans-serif;
    font-size: 14.5px;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
    border: 1px solid transparent;
    transition: transform 0.16s ease, filter 0.16s ease;
}
body.v2 .v2-btn       { background: var(--grad); color: #fff; }
body.v2 .v2-btn-ghost { background: rgba(255,255,255,0.06); border-color: var(--line-strong); color: var(--t1); font-weight: 600; }
body.v2 .v2-btn-light { background: #ffffff; color: #380838; }
body.v2 .v2-btn:hover,
body.v2 .v2-btn-light:hover { transform: translateY(-1px); filter: brightness(1.06); }
body.v2 .v2-btn-ghost:hover { background: rgba(255,255,255,0.1); }
body.v2 .v2-btn-lg { min-height: 52px; padding: 15px 30px; font-size: 15.5px; border-radius: 14px; }
body.v2 .v2-btn-block { display: flex; width: 100%; box-sizing: border-box; }

/* ── HERO ───────────────────────────────────────────────────────────
   The banner Ken asked to keep. Gradient field, art on the right,
   text on the left; centered and stacked on a phone.
   ------------------------------------------------------------------ */
body.v2 .v2-hero { position: relative; overflow: hidden; }
body.v2 .v2-hero::before {
    content: '';
    position: absolute; inset: 0;
    /* TWO layers. The diagonal is the hero color; the vertical one
       fades it into the page background at the bottom.

       Without the second layer the gradient ends abruptly at the
       hero's own edge, and because the diagonal is still deep purple
       on the left at that point, it met the near-black page in a hard
       vertical seam beside the stats bar. A gradient that stops is
       still an edge.

       THE DARK TOP-LEFT CORNER is the point of the diagonal, and the
       first cut of it missed that. v1's body carried
       linear-gradient(135deg, #000, #570a57, #0a0510): it opened on
       black in the top-left, built to full purple across the middle,
       and fell away again to near-black at the bottom-right. The
       purple reads as light falling across the page rather than as a
       flat wash, and the near-black corner is what gives the whole
       thing depth.

       This started at 120deg from an already-lit #380838, so there
       was no dark corner at all and the banner sat there evenly
       purple. Same angle as v1 now, and it opens darker than the page
       background so the corner reads as a vignette rather than as the
       page showing through. */
    background:
        linear-gradient(to bottom, rgba(22,6,22,0) 62%, var(--bg) 100%),
        linear-gradient(135deg, #100310 0%, #2b0a2b 20%, #5c0d5c 52%, #3b0a3b 76%, var(--bg) 100%);
}

/* The hero runs UP behind the fixed navbar rather than starting below
   it. .v2-main pads the page down by the navbar's height so ordinary
   pages clear it — but a hero should not, or the reader gets a band
   of bare page background between the navbar and the artwork, which
   is what shipped.

   Pulling it up by the same amount and padding the content back down
   also gives the translucent navbar something to blur, which is the
   whole point of it being translucent. */
body.v2 .v2-main > .v2-hero:first-child {
    margin-top: -76px;
    /* No padding here: .v2-hero-inner does the clearing, so the
       artwork can rise behind the navbar while the text does not. */
    padding-top: 0;
}
body.v2 .v2-hero::after {
    content: '';
    position: absolute; top: -180px; right: 60px;
    width: 900px; height: 900px;
    background: radial-gradient(circle at center, rgba(219,70,230,0.28) 0%, rgba(219,70,230,0) 62%);
    pointer-events: none;
}
body.v2 .v2-hero-inner {
    position: relative; z-index: 2;
    box-sizing: border-box;
    /* The banner stays edge to edge; only the row inside it is capped.

       Without this the hero was the one section on the site with no
       bound at all, so on a wide monitor "Build the Empire." sat
       against the left wall with the artwork pushed to the far right
       and a lake of gradient between them.

       1440 is picked from the parts, not from a round number: 610 of
       text, a 40 gap, 112 of side padding and the artwork's own 700
       ceiling come to 1462, so at 1440 the art lands at 678 and is
       the only thing that gives, by 22px. Below 1440 nothing changes
       at all, which is the point: the complaint was only ever about
       wide screens. */
    max-width: 1440px;
    margin-inline: auto;
    /* 76px navbar + 18px breathing room. Both columns start below
       this line. The navbar still reads as floating ON the banner,
       because .v2-hero's own gradient runs up behind it. */
    padding: 94px var(--pad) 84px;
    display: flex;
    align-items: center;
    gap: 40px;
}
body.v2 .v2-hero-text { width: 610px; flex-shrink: 0; }
body.v2 .v2-hero-art  { flex-grow: 1; display: flex; align-items: center; justify-content: center; }
body.v2 .v2-hero-art img {
    width: 100%; max-width: 700px; height: auto; display: block;
    filter: drop-shadow(0 30px 60px rgba(0,0,0,0.45));
    /* NO negative margin here.

       The previous version lifted the artwork 58px into the navbar's
       band on the theory that a translucent navbar would let it read
       through. It does not. At 55% opacity over a 20px blur the top
       of the illustration turns to mush, and the floating icons along
       its top edge come out looking sliced off, which is exactly what
       Ken reported.

       What has to run up behind the navbar is the hero BACKGROUND,
       and that still does: .v2-hero itself starts at y=0. The navbar
       gets its gradient to blur and the artwork keeps its top edge. */
    margin-top: 0;

    /* Drifts, the way the v1 city image did. v1 used 3s and 25px of
       travel, which is a bob rather than a drift, so this is slower
       and shorter.

       The swing is centered on the resting position (+8 down, -8 up)
       rather than the usual 0 to -16. A one-sided lift would take the
       art's top edge from 94px up to 78px, and the navbar ends at
       76px, so it would graze the thing we just stopped it touching.
       Centred, the top edge travels between 86px and 102px and never
       comes within 10px of the navbar. */
    animation: v2HeroFloat 7s ease-in-out infinite;
    will-change: transform;
}
@keyframes v2HeroFloat {
    0%, 100% { transform: translateY(8px); }
    50%      { transform: translateY(-8px); }
}

/* ── THE PARTICIPATION GRID ──────────────────────────────────────
   See the note in index.php for why this is three nested elements
   rather than one. Short version: each level carries exactly one
   mask, and the dot mask lives on the PARENT so the ripples scale
   through a grid that stays still.

   Everything here is transform and opacity, so it runs on the
   compositor and the main thread never wakes for it. No script, no
   canvas, no request, no @property. */
body.v2 .v2-hero-fx {
    position: absolute; inset: 0;
    overflow: hidden;
    pointer-events: none;
    /* The hero's own gradient fades to --bg over its bottom third.
       Without this the dots would keep going straight past that fade
       and sit on the flat page colour under the stats bar. */
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 52%, transparent 94%);
            mask-image: linear-gradient(to bottom, #000 0%, #000 52%, transparent 94%);
}
body.v2 .v2-hero-dots {
    position: absolute; inset: 0;
    -webkit-mask-image: radial-gradient(circle, #000 1.2px, transparent 1.6px);
            mask-image: radial-gradient(circle, #000 1.2px, transparent 1.6px);
    -webkit-mask-size: 26px 26px;
            mask-size: 26px 26px;
}
/* The resting field. Flat colour; the mask above is what makes it
   dots, which is also why a ripple only ever lights dots and never
   the gaps between them. */
body.v2 .v2-hero-dotfill {
    position: absolute; inset: 0;
    background: rgba(240, 165, 246, 0.17);
}
body.v2 .v2-hero-ripple {
    position: absolute;
    width: 700px; height: 700px;
    margin: -350px 0 0 -350px;
    border-radius: 50%;
    /* closest-side, and it is load-bearing. A radial-gradient defaults
       to farthest-corner, so on a 700px square 100% is the 495px
       corner distance while border-radius clips at 350. The bright
       part of the ring sat at 77%, which is 381px, entirely outside
       the clip: the first build rendered three invisible rings and a
       faint smudge. closest-side makes 100% the 350px edge, so the
       whole band lands inside the circle. */
    /* The peak is a PLATEAU, not a point, and it is white rather than
       accent. A single bright stop lights one row of dots and reads as
       a smudge from two feet away; two stops at the same value light
       four or five rows and read as a ring. White because the resting
       field is already accent-coloured, so a brighter magenta barely
       separates from it while white separates completely. */
    background: radial-gradient(circle closest-side,
        transparent 48%,
        rgba(219, 70, 230, 0.45) 64%,
        rgba(255, 255, 255, 0.95) 73%,
        rgba(255, 255, 255, 0.95) 80%,
        rgba(219, 70, 230, 0.45) 89%,
        transparent 99%);
    opacity: 0;
    animation: v2HeroRipple 11s linear infinite;
}
/* Kept off the headline. The left half of the banner is type, so the
   ring starts low there and the other two work the open right side. */
body.v2 .v2-hero-ripple.r1 { left: 28%; top: 74%; animation-delay: 0s; }
body.v2 .v2-hero-ripple.r2 { left: 66%; top: 30%; animation-delay: 3.7s; }
body.v2 .v2-hero-ripple.r3 { left: 87%; top: 62%; animation-delay: 7.4s; }
@keyframes v2HeroRipple {
    0%   { transform: scale(0.06); opacity: 0; }
    14%  { opacity: 1; }
    100% { transform: scale(3.2);  opacity: 0; }
}
body.v2 .v2-hero h1 {
    margin: 0 0 22px;
    font-size: clamp(2.35rem, 4.4vw, 3.6rem);
    line-height: 1.06;
    font-weight: 700;
    letter-spacing: -1.6px;
    color: #fff;
}
body.v2 .v2-hero-sub {
    margin: 0 0 36px;
    font-size: clamp(1rem, 1.3vw, 1.125rem);
    line-height: 1.65;
    color: #e8d0e8;
    max-width: 540px;
}
body.v2 .v2-hero-btns { display: flex; gap: 14px; margin-bottom: 40px; }

@media (max-width: 900px) {
    /* Navbar is 64px here, so the hero's pull-up has to match it. */
    body.v2 .v2-main > .v2-hero:first-child { margin-top: -64px; padding-top: 0; }
    /* column-reverse puts the art FIRST visually, so its own top
       padding is what clears the 64px navbar - 64 + 10. */
    body.v2 .v2-hero-inner { flex-direction: column-reverse; text-align: center; padding: 74px var(--pad) 56px; gap: 18px; }
    body.v2 .v2-hero-text  { width: 100%; }
    /* Same rule as desktop: the art clears the navbar, the background
       does not have to. 74px of padding above it, 64px of navbar. */
    /* Only 10px between the art and the navbar here (74 against 64),
       so the swing drops to +5 / -5. Top edge travels 69px to 79px. */
    body.v2 .v2-hero-art img { max-width: 330px; margin-top: 0; animation-name: v2HeroFloatSm; }
    @keyframes v2HeroFloatSm {
        0%, 100% { transform: translateY(5px); }
        50%      { transform: translateY(-5px); }
    }
    body.v2 .v2-hero-sub   { margin-left: auto; margin-right: auto; }
    body.v2 .v2-hero-btns  { flex-direction: column; }
    body.v2 .v2-hero::after { top: -120px; right: -140px; width: 520px; height: 520px; }

    /* The ripple is drawn as a proportion of its own box, so the desktop
       size puts the ring at a 784px radius with a 272px band at full
       scale. On a 375px screen that is not a ring passing through, it is
       the whole screen going light and then dark. Smaller box, shorter
       throw: the band runs 30px to 73px and still reads as a wave. */
    body.v2 .v2-hero-ripple {
        width: 360px; height: 360px; margin: -180px 0 0 -180px;
        animation-name: v2HeroRippleSm;
    }
    @keyframes v2HeroRippleSm {
        0%   { transform: scale(0.08); opacity: 0; }
        14%  { opacity: 1; }
        100% { transform: scale(2.4);  opacity: 0; }
    }
    /* Tighter field on a narrow screen, so it does not turn into
       wallpaper behind centred text. */
    body.v2 .v2-hero-dots {
        -webkit-mask-size: 22px 22px;
                mask-size: 22px 22px;
    }
    /* Two, not three, and both clear of the paragraph.

       The desktop layout has an open right half to work; the mobile
       one is column-reverse and centred, so every row of the banner
       is type. Three rings on an 11s cycle put a bright arc through
       the body copy more often than not. One sits below the buttons
       and one behind the artwork, which covers most of it anyway. */
    body.v2 .v2-hero-ripple.r1 { left: 22%; top: 87%; animation-delay: 0s; }
    body.v2 .v2-hero-ripple.r2 { left: 78%; top: 13%; animation-delay: 5.5s; }
    body.v2 .v2-hero-ripple.r3 { display: none; }
}

/* ── EYEBROW PILL ───────────────────────────────────────────────── */
body.v2 .v2-pill {
    display: inline-flex; align-items: center; gap: 10px;
    padding: 7px 16px;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: var(--surface-2);
    font-size: 12.5px; font-weight: 700; letter-spacing: 0.9px;
    color: #fff;
    margin-bottom: 26px;
}
/* v1's dot pulsed and the rebuild dropped it. v1 used a step-end
   blink, which is an on/off flicker; this breathes instead, which
   reads as "live" rather than "broken indicator". The glow carries
   most of the motion so the dot never fully disappears. */
body.v2 .v2-dot {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--good);
    flex-shrink: 0;
    animation: v2DotPulse 2.4s ease-in-out infinite;
}
@keyframes v2DotPulse {
    0%, 100% { opacity: 1;    box-shadow: 0 0 0 0 rgba(61,220,151,0.55); }
    50%      { opacity: 0.72; box-shadow: 0 0 0 4px rgba(61,220,151,0); }
}
/* Anyone who has asked their system for less motion gets a steady
   dot rather than none at all. */
@media (prefers-reduced-motion: reduce) {
    body.v2 .v2-dot { animation: none; }
}

/* ── STAT BAR ───────────────────────────────────────────────────────
   Lifted up over the hero's bottom edge. The one flourish on the page.
   ------------------------------------------------------------------ */
body.v2 .v2-stats {
    position: relative; z-index: 3;
    box-sizing: border-box;
    padding: 0 var(--pad);
    margin-top: -46px;
}
body.v2 .v2-stats-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 1px;
    background: var(--line);
    border: 1px solid rgba(255,255,255,0.14);
    border-radius: 20px;
    overflow: hidden;
    box-shadow: 0 24px 60px rgba(0,0,0,0.45);
}
body.v2 .v2-stat { box-sizing: border-box; padding: 26px 30px; background: var(--bg-raised); text-align: center; }
body.v2 .v2-stat-num { display: block; font-size: 27px; font-weight: 700; letter-spacing: -0.6px; color: var(--t1); margin-bottom: 6px; }
body.v2 .v2-stat-num.is-pending { color: var(--warn); }
body.v2 .v2-stat-label { display: block; font-size: 13px; font-weight: 600; letter-spacing: 0.6px; color: var(--t3); }

@media (max-width: 900px) {
    body.v2 .v2-stats { margin-top: -34px; }
    body.v2 .v2-stats-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); border-radius: 18px; }
    body.v2 .v2-stat { padding: 18px 14px; }
    body.v2 .v2-stat-num { font-size: 19px; }
    body.v2 .v2-stat-label { font-size: 11.5px; }
}

/* ── SECTIONS ───────────────────────────────────────────────────────
   Headings center at every width. align-items is set as well as
   text-align because these containers go column-direction on a phone,
   where justify-content runs vertically and centers nothing.
   ------------------------------------------------------------------ */
body.v2 .v2-section { box-sizing: border-box; padding: 88px var(--pad) 0; }
body.v2 .v2-section-head { text-align: center; margin-bottom: 44px; display: flex; flex-direction: column; align-items: center; }
body.v2 .v2-eyebrow { font-size: 13px; font-weight: 700; letter-spacing: 2.4px; color: var(--accent); margin-bottom: 14px; }
body.v2 .v2-section-head h2 {
    margin: 0 0 14px;
    font-size: clamp(1.6rem, 2.9vw, 2.375rem);
    line-height: 1.2;
    font-weight: 700;
    letter-spacing: -1px;
    color: #fff;
}
body.v2 .v2-section-sub { margin: 0; font-size: clamp(0.94rem, 1.2vw, 1.03rem); line-height: 1.65; color: var(--t2); max-width: 640px; }

@media (max-width: 900px) {
    body.v2 .v2-section { padding-top: 46px; }
    body.v2 .v2-section-head { margin-bottom: 28px; }
    body.v2 .v2-eyebrow { font-size: 11.5px; letter-spacing: 2.2px; }
}

/* ── CARDS ──────────────────────────────────────────────────────── */
body.v2 .v2-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 22px; }
body.v2 .v2-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; }

body.v2 .v2-card {
    box-sizing: border-box;
    padding: 38px 30px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    text-align: center;
}
body.v2 .v2-card h3 { margin: 0 0 12px; font-size: 21px; font-weight: 700; color: #fff; }
body.v2 .v2-card p  { margin: 0; font-size: 15px; line-height: 1.7; color: var(--t2); }
body.v2 .v2-card-icon {
    width: 56px; height: 56px;
    margin: 0 auto 22px;
    border-radius: 16px;
    display: flex; align-items: center; justify-content: center;
}

@media (max-width: 900px) {
    body.v2 .v2-grid-3, body.v2 .v2-grid-2 { grid-template-columns: minmax(0, 1fr); gap: 14px; }
    body.v2 .v2-card { padding: 26px 22px; border-radius: 20px; }
    body.v2 .v2-card h3 { font-size: 19px; }
    body.v2 .v2-card p  { font-size: 14.5px; }
}

/* ── PANEL (a card that holds its own layout, eg. a proposal) ────── */
body.v2 .v2-panel {
    box-sizing: border-box;
    padding: 34px 38px;
    border: 1px solid rgba(255,255,255,0.12);
    border-radius: 24px;
    background: var(--surface);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    text-align: left;
}
@media (max-width: 900px) { body.v2 .v2-panel { padding: 22px; border-radius: 20px; } }

body.v2 .v2-tag {
    display: inline-flex; align-items: center;
    padding: 4px 11px; border-radius: 8px;
    font-size: 11.5px; font-weight: 700; letter-spacing: 0.8px;
}
/* A tag that is also a link needs a real touch target without
   growing into a button. The pseudo-element expands the hit area to
   44px around it while the tag keeps its own size. /profile's
   "NUMBER 1" tag measured 23px tall before this. */
body.v2 a.v2-tag { position: relative; text-decoration: none; }
body.v2 a.v2-tag::after {
    content: '';
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 100%; min-width: 44px;
    height: 44px;
}
body.v2 a.v2-tag:hover { filter: brightness(1.15); }

body.v2 .v2-tag.is-good { background: var(--good-bg); color: var(--good); }
body.v2 .v2-tag.is-warn { background: rgba(255,179,64,0.16); color: var(--warn); }
body.v2 .v2-tag.is-bad  { background: var(--bad-bg);  color: var(--bad); }
body.v2 .v2-tag.is-idle { background: rgba(255,255,255,0.07); color: var(--t3); }

body.v2 .v2-meter { height: 10px; border-radius: 5px; background: rgba(255,255,255,0.1); overflow: hidden; }
body.v2 .v2-meter span { display: block; height: 10px; border-radius: 5px; }

body.v2 .v2-notice {
    display: flex; align-items: center; gap: 12px;
    padding: 15px 18px;
    border: 1px solid rgba(255,179,64,0.32);
    border-radius: 14px;
    background: var(--warn-bg);
    font-size: 14.5px;
    color: #ffd9a3;
}
/* A flex item's default min-width is its content, but an SVG has no
   intrinsic content to hold the floor, so it shrinks to a smudge
   whenever the text beside it wraps. Every notice on the site is
   one line on desktop and three or four at 375px, which is why this
   only ever showed up on a phone. */
body.v2 .v2-notice svg { flex-shrink: 0; }

/* ── CTA BAND ───────────────────────────────────────────────────── */
body.v2 .v2-cta {
    position: relative;
    box-sizing: border-box;
    padding: 54px;
    border-radius: 26px;
    overflow: hidden;
    text-align: center;
    border: 1px solid rgba(255,255,255,0.14);
}
body.v2 .v2-cta::before {
    content: '';
    position: absolute; inset: 0;
    /* Deliberately NOT the hero's dark-corner diagonal. This is a
       small rounded card sitting on the page rather than a full-width
       band bleeding off it, and a dark corner inside 26px of border
       radius reads as a smudge, not as depth. It opens bright and
       falls away, which is what makes the card lift off the page.
       Left alone on purpose; not an oversight. */
    background: linear-gradient(120deg, #5c0d5c 0%, #380838 55%, #2b0a2b 100%);
}
body.v2 .v2-cta-inner { position: relative; z-index: 2; }
body.v2 .v2-cta h2 { margin: 0 0 14px; font-size: clamp(1.5rem, 2.4vw, 2.125rem); font-weight: 700; letter-spacing: -0.9px; color: #fff; }
body.v2 .v2-cta p  { margin: 0 auto 30px; font-size: clamp(0.94rem, 1.2vw, 1.03rem); line-height: 1.6; color: #e8d0e8; max-width: 520px; }
body.v2 .v2-cta-btns { display: flex; gap: 14px; justify-content: center; }
@media (max-width: 900px) {
    body.v2 .v2-cta { padding: 34px 22px; border-radius: 22px; }
    body.v2 .v2-cta-btns { flex-direction: column; }
}

/* ══════════════════════════════════════════════════════════════════
   INNER PAGE COMPONENTS

   Everything above this line was written for the homepage. Everything
   below is shared by the pages behind it, starting with /learn.

   The rule for adding to this section: a component goes in only when a
   second page would use it. One page's layout belongs in that page.
   ══════════════════════════════════════════════════════════════════ */

/* ── PAGE HEAD ──────────────────────────────────────────────────────
   The inner-page equivalent of the homepage hero. Same gradient, same
   trick of running up behind the navbar so the blur has something to
   work on, but no artwork and roughly half the height.

   This exists so the pages cannot drift. In v1 /buy and /learn each
   defined their own heading block, and they ended up with different
   h1 sizes and different mobile alignment. One component, used by
   every inner page, is the fix for that.
   ------------------------------------------------------------------ */
body.v2 .v2-page-head { position: relative; overflow: hidden; }
body.v2 .v2-page-head::before {
    content: '';
    position: absolute; inset: 0;
    background:
        /* Same dark-corner diagonal as the hero, shifted a couple of
           points later because this band is shorter. See the note on
           .v2-hero::before for why the corner matters. */
        linear-gradient(to bottom, rgba(22,6,22,0) 58%, var(--bg) 100%),
        linear-gradient(135deg, #100310 0%, #2b0a2b 22%, #5c0d5c 54%, #3b0a3b 78%, var(--bg) 100%);
}
/* closest-side, and the whole blob kept inside the right edge.
   `circle at center` defaults to farthest-corner, so on a 720px box
   the gradient did not reach zero until 0.62 x 509 = 316px from the
   centre, while the centre sat only 280px from the parent's right
   edge. .v2-page-head has overflow:hidden, so the glow was sliced
   off mid-fade and left a visible vertical seam down the right of
   every interior page. closest-side pins the radius to half the box,
   and the geometry below puts the fade well inside the clip.

   The hero's glow has the same shape but its centre sits 510px in
   against a 394px fade, so it never clipped and is left alone. */
body.v2 .v2-page-head::after {
    content: '';
    position: absolute; top: -320px; right: -60px;
    width: 1000px; height: 1000px;
    background: radial-gradient(circle closest-side, rgba(219,70,230,0.22) 0%, rgba(219,70,230,0) 62%);
    pointer-events: none;
}
/* Same pull-up as the hero, for the same reason. */
body.v2 .v2-main > .v2-page-head:first-child { margin-top: -76px; padding-top: 0; }
body.v2 .v2-page-head-inner {
    position: relative; z-index: 2;
    box-sizing: border-box;
    /* 76px of navbar, then the content. */
    padding: 128px var(--pad) 62px;
    display: flex; flex-direction: column; align-items: center;
    text-align: center;
}
body.v2 .v2-page-head h1 {
    margin: 0 0 16px;
    font-size: clamp(2rem, 3.5vw, 2.9rem);
    line-height: 1.12;
    font-weight: 700;
    letter-spacing: -1.3px;
    color: #fff;
}
body.v2 .v2-page-head-sub {
    margin: 0;
    font-size: clamp(0.97rem, 1.25vw, 1.08rem);
    line-height: 1.68;
    color: #e8d0e8;
    max-width: 620px;
}
@media (max-width: 900px) {
    body.v2 .v2-main > .v2-page-head:first-child { margin-top: -64px; }
    body.v2 .v2-page-head-inner { padding: 100px var(--pad) 42px; }
    body.v2 .v2-page-head::after { top: -180px; right: -180px; width: 480px; height: 480px; }
}

/* ── PROSE ──────────────────────────────────────────────────────────
   Body copy inside a panel. .v2-card styles its own <p>; .v2-panel
   deliberately does not, because a panel holds arbitrary layout. This
   is the opt-in for when a panel is holding plain text.
   ------------------------------------------------------------------ */
body.v2 .v2-prose { margin: 0; font-size: 15.5px; line-height: 1.75; color: var(--t2); }
body.v2 .v2-prose + .v2-prose { margin-top: 14px; }
body.v2 .v2-prose strong { color: #fff; font-weight: 700; }
body.v2 .v2-lede { font-size: clamp(1.02rem, 1.35vw, 1.15rem); line-height: 1.8; color: #e2d6ea; }

/* ── FACT GRID ──────────────────────────────────────────────────────
   The stat bar's sibling. Same joined-cell look, but it stands on its
   own in the page instead of straddling the hero's bottom edge, and it
   takes any number of columns.
   ------------------------------------------------------------------ */
/* is-4 for a four-fact row. Without it the fourth wraps to a second
   row of one, and the joined-cell look turns that into an empty box
   beside it. */
body.v2 .v2-facts.is-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
body.v2 .v2-facts {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1px;
    background: var(--line);
    border: 1px solid rgba(255,255,255,0.14);
    border-radius: 20px;
    overflow: hidden;
}
body.v2 .v2-fact {
    box-sizing: border-box;
    padding: 26px 22px;
    background: var(--bg-raised);
    text-align: center;
    /* Grid already stretches every cell in a row to the height of the
       tallest one. Centring the content inside means a label that
       wraps makes its row taller and its neighbours stay level with
       it, which is what the min-height below used to be for. */
    display: flex;
    flex-direction: column;
    justify-content: center;
}
body.v2 .v2-fact-num { display: block; font-size: 22px; font-weight: 700; letter-spacing: -0.4px; color: var(--t1); margin-bottom: 7px; }
body.v2 .v2-fact-label {
    display: block;
    font-size: 12.5px; font-weight: 600; letter-spacing: 0.5px;
    color: var(--t3);
    line-height: 1.45;
    /* No min-height. It used to reserve two lines here so that one
       long label could not make its row deeper than the row above,
       but that paid for the fix on every card: a single line label
       carried a line and a half of empty space under it on eight
       pages. The flex centring on .v2-fact solves the same problem
       without charging every card for it. */
}
@media (max-width: 900px) {
    /* .is-4 is listed here too, and this is not belt and braces.
       `body.v2 .v2-facts.is-4` is (0,3,0) and `body.v2 .v2-facts` is
       (0,2,0), so the desktop four-column rule beats this one on
       specificity and a media query does nothing about that. Four
       columns would have survived down to 375px. */
    body.v2 .v2-facts,
    body.v2 .v2-facts.is-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); border-radius: 18px; }
    body.v2 .v2-fact { padding: 18px 14px; }
    body.v2 .v2-fact-num { font-size: 17px; }
    body.v2 .v2-fact-label { font-size: 11.5px; }
    /* An odd number of facts leaves a hole in a two-column grid, and
       because the cells are separated by a 1px gap over a colored
       background that hole renders as an empty box rather than as
       nothing. /governance has three. The orphan takes the whole
       last row instead.

       :last-child:nth-child(odd) is the test for "sits alone on the
       final row of a two-column grid". /learn's six facts are
       untouched by it. */
    body.v2 .v2-fact:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* ── TWO THINGS THAT BOTH HAVE TO HAPPEN ────────────────────────────
   Two panels with a word between them. Used for the vote, where a
   proposal needs both groups, and reusable anywhere else the answer is
   "both of these, not either of these".

   The joining word is a grid cell rather than a pseudo-element so it
   can be read aloud, and so it moves to its own row on a phone instead
   of being clipped.
   ------------------------------------------------------------------ */
body.v2 .v2-both {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    gap: 16px;
    align-items: stretch;
}
body.v2 .v2-both-join {
    display: flex; align-items: center; justify-content: center;
    font-size: 12.5px; font-weight: 800; letter-spacing: 1.8px;
    color: var(--accent);
    white-space: nowrap;
}
body.v2 .v2-group-head { display: flex; align-items: center; gap: 13px; margin-bottom: 15px; }
body.v2 .v2-group-head h3 { margin: 0; font-size: 18.5px; font-weight: 700; color: #fff; }
body.v2 .v2-group-ico {
    width: 42px; height: 42px;
    border-radius: 12px;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}
@media (max-width: 900px) {
    body.v2 .v2-both { grid-template-columns: minmax(0, 1fr); gap: 12px; }
    body.v2 .v2-both-join { padding: 4px 0; }
}

/* ── ROADMAP ────────────────────────────────────────────────────────
   One rail down the left at every width.

   v1 ran the rail down the middle on desktop and moved it left on a
   phone, which meant two layouts to keep working and a set of negative
   margins holding the badges onto the line. A single left rail reads
   the same on both and there is only one thing to get right.

   Geometry, because it is easy to break: the container pads 40px on
   the left, so a phase starts at x=40. The rail sits at x=12 with a
   2px width, center x=13. The dot is 22px wide at left:-38px, which
   puts its left edge at 40-38=2 and its center at 2+11=13. Change any
   one of those four numbers and the dots leave the line.
   ------------------------------------------------------------------ */
body.v2 .v2-road { position: relative; max-width: 740px; margin: 0 auto; padding-left: 40px; }
body.v2 .v2-road::before {
    content: '';
    position: absolute; left: 12px; top: 10px; bottom: 10px; width: 2px;
    background: linear-gradient(to bottom, var(--accent) 0%, rgba(219,70,230,0.5) 45%, rgba(219,70,230,0.08) 100%);
}
body.v2 .v2-road-phase { position: relative; margin-bottom: 18px; }
body.v2 .v2-road-phase:last-child { margin-bottom: 0; }
body.v2 .v2-road-dot {
    position: absolute; left: -38px; top: 28px;
    width: 22px; height: 22px;
    border-radius: 50%;
    border: 2px solid var(--bg);
    background: var(--bg-raised);
    display: flex; align-items: center; justify-content: center;
}
body.v2 .v2-road-dot.is-done { background: var(--good); }
body.v2 .v2-road-dot.is-now  { background: var(--warn); }
body.v2 .v2-road-badge {
    display: inline-flex; align-items: center;
    padding: 5px 12px; border-radius: 999px;
    font-size: 11px; font-weight: 800; letter-spacing: 1.3px;
    margin-bottom: 13px;
}
body.v2 .v2-road-badge.is-done { background: var(--good-bg); color: var(--good); }
body.v2 .v2-road-badge.is-now  { background: rgba(255,179,64,0.16); color: var(--warn); }
body.v2 .v2-road-badge.is-next { background: rgba(255,255,255,0.07); color: var(--t3); }
body.v2 .v2-road-phase h3 { margin: 0 0 8px; font-size: 19px; font-weight: 700; color: #fff; }
body.v2 .v2-road-list { list-style: none; margin: 15px 0 0; padding: 0; }
body.v2 .v2-road-list li {
    display: flex; align-items: flex-start; gap: 11px;
    padding: 7px 0;
    font-size: 14.5px; line-height: 1.5;
    color: var(--t3);
}
body.v2 .v2-road-list li.is-done { color: var(--t1); }
body.v2 .v2-road-list svg { flex-shrink: 0; margin-top: 2px; }
@media (max-width: 900px) {
    body.v2 .v2-road { padding-left: 34px; }
    body.v2 .v2-road::before { left: 9px; }
    body.v2 .v2-road-dot { left: -34px; width: 20px; height: 20px; top: 24px; }
    body.v2 .v2-road-list li { font-size: 14px; }
}

/* ── QUESTIONS ──────────────────────────────────────────────────────
   Real <details> elements. They open without JavaScript, they are
   keyboard operable for free, and a phone does not have to render four
   paragraphs the reader has not asked for yet.
   ------------------------------------------------------------------ */
body.v2 .v2-faq { max-width: 740px; margin: 0 auto; display: flex; flex-direction: column; gap: 11px; }
body.v2 .v2-faq-item {
    border: 1px solid var(--line);
    border-radius: 16px;
    background: var(--surface);
    overflow: hidden;
}
body.v2 .v2-faq-item summary {
    box-sizing: border-box;
    list-style: none;
    cursor: pointer;
    min-height: 44px;
    padding: 17px 20px;
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    font-size: 16px; font-weight: 700; color: #fff;
}
body.v2 .v2-faq-item summary::-webkit-details-marker { display: none; }
body.v2 .v2-faq-item summary::after {
    content: '+';
    font-size: 21px; font-weight: 400; line-height: 1;
    color: var(--accent);
    flex-shrink: 0;
}
/* U+2212 MINUS SIGN, which matches the plus optically. A hyphen does
   not: it sits high and short next to a full-height plus. */
body.v2 .v2-faq-item[open] summary::after { content: '\2212'; }
body.v2 .v2-faq-item[open] summary { color: var(--accent-soft); }
body.v2 .v2-faq-body { padding: 0 20px 19px; }
@media (max-width: 900px) {
    body.v2 .v2-faq-item summary { font-size: 15px; padding: 15px 17px; }
    body.v2 .v2-faq-body { padding: 0 17px 17px; }
}

/* ── FILTER PILLS ───────────────────────────────────────────────────
   A row of toggles over a list. 44px tall because they are the first
   thing a thumb reaches for on /governance.
   ------------------------------------------------------------------ */
body.v2 .v2-filters { display: flex; flex-wrap: wrap; gap: 9px; }
body.v2 .v2-filter {
    box-sizing: border-box;
    min-height: 44px;
    padding: 10px 19px;
    border-radius: 999px;
    border: 1px solid var(--line-strong);
    background: rgba(255,255,255,0.04);
    color: var(--t2);
    font-family: 'Quicksand', sans-serif;
    font-size: 13.5px; font-weight: 700; letter-spacing: 0.4px;
    cursor: pointer;
    transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
/* The count on a chip. Quieter than the label, because it is the
   second thing you read, and tabular figures so 1 and 11 do not
   shuffle the label sideways. */
body.v2 .v2-filter-n {
    margin-left: 7px;
    padding: 1px 6px;
    border-radius: 999px;
    background: rgba(255,255,255,0.09);
    font-size: 12px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--t3);
}
body.v2 .v2-filter.is-on .v2-filter-n { background: rgba(219,70,230,0.22); color: var(--accent-soft); }
body.v2 .v2-filter:hover { color: #fff; background: rgba(255,255,255,0.08); }
body.v2 .v2-filter.is-on {
    border-color: var(--accent);
    background: rgba(219,70,230,0.14);
    color: #fff;
}

/* ── LIST HEAD ──────────────────────────────────────────────────────
   A heading on the left and controls on the right, stacking to two
   rows on a phone. NOT .v2-section-head, which centers.
   ------------------------------------------------------------------ */
body.v2 .v2-list-head {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 20px; flex-wrap: wrap;
    margin-bottom: 26px;
}
body.v2 .v2-list-head h2 {
    margin: 0 0 8px;
    font-size: clamp(1.4rem, 2.3vw, 1.9rem);
    font-weight: 700; letter-spacing: -0.8px; color: #fff;
}
body.v2 .v2-list-head p { margin: 0; font-size: 14.5px; color: var(--t3); }
@media (max-width: 900px) {
    body.v2 .v2-list-head { align-items: flex-start; gap: 16px; margin-bottom: 18px; }
}

/* ── PROPOSAL CARD ──────────────────────────────────────────────────
   The main column carries the argument and the counting; the side
   column carries the one thing you can DO about it. They swap to a
   single column on a phone with the action column FIRST, because on a
   phone the button is what you came for and it should not be four
   screens down.
   ------------------------------------------------------------------ */
body.v2 .v2-props { display: flex; flex-direction: column; gap: 18px; }
body.v2 .v2-prop {
    box-sizing: border-box;
    border: 1px solid var(--line);
    border-radius: 24px;
    background: var(--surface);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    overflow: hidden;
    scroll-margin-top: 96px;
}
body.v2 .v2-prop.is-highlight { border-color: var(--accent); box-shadow: 0 0 0 1px rgba(219,70,230,0.45); }
body.v2 .v2-prop-grid { display: grid; grid-template-columns: minmax(0, 1fr) 300px; }
body.v2 .v2-prop-main { box-sizing: border-box; padding: 30px 32px; min-width: 0; }
body.v2 .v2-prop-side {
    box-sizing: border-box;
    padding: 30px 26px;
    border-left: 1px solid var(--line);
    background: rgba(255,255,255,0.022);
    display: flex; flex-direction: column; justify-content: center;
}
@media (max-width: 900px) {
    body.v2 .v2-prop { border-radius: 20px; }
    body.v2 .v2-prop-grid { grid-template-columns: minmax(0, 1fr); }
    /* Action first on a phone. order, not DOM order, so the reading
       order for a screen reader stays argument-then-action. */
    body.v2 .v2-prop-main { order: 2; padding: 22px 20px; }
    body.v2 .v2-prop-side { order: 1; padding: 20px; border-left: none; border-bottom: 1px solid var(--line); }
}

body.v2 .v2-prop-tags { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
body.v2 .v2-prop-num { font-size: 13px; font-weight: 700; letter-spacing: 1.1px; color: var(--t3); }
body.v2 .v2-prop h3 { margin: 0 0 12px; font-size: 23px; line-height: 1.26; font-weight: 700; color: #fff; }
body.v2 .v2-prop-desc { margin: 0 0 24px; font-size: 15px; line-height: 1.7; color: var(--t2); }
body.v2 .v2-prop-meta {
    display: flex; flex-wrap: wrap; gap: 8px 22px;
    margin-top: 20px;
    font-size: 13.5px; font-weight: 600; color: var(--t3);
}
body.v2 .v2-prop-meta .is-live { color: var(--accent-soft); }
@media (max-width: 900px) {
    body.v2 .v2-prop h3 { font-size: 19.5px; }
    body.v2 .v2-prop-desc { font-size: 14.5px; margin-bottom: 20px; }
}

/* ── THE COUNT ──────────────────────────────────────────────────────
   One block per group. .v2-meter is a single fill, which cannot show
   a split, so the track here holds two.
   ------------------------------------------------------------------ */
body.v2 .v2-count { display: flex; flex-direction: column; gap: 20px; }
body.v2 .v2-count-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 9px; }
body.v2 .v2-count-name { font-size: 14px; font-weight: 700; color: #fff; }
body.v2 .v2-split {
    display: flex;
    height: 10px;
    border-radius: 5px;
    overflow: hidden;
    background: rgba(255,255,255,0.09);
}
body.v2 .v2-split span { display: block; height: 10px; }
body.v2 .v2-split .is-yes { background: var(--good); }
body.v2 .v2-split .is-no  { background: var(--bad); }
body.v2 .v2-count-foot {
    display: flex; align-items: center; flex-wrap: wrap; gap: 4px 14px;
    margin-top: 9px;
    font-size: 13px; color: var(--t3);
}
body.v2 .v2-count-foot .is-yes { color: var(--good); font-weight: 700; }
body.v2 .v2-count-foot .is-no  { color: var(--bad);  font-weight: 700; }
body.v2 .v2-count-foot .is-short { color: var(--warn); }

/* ── VERDICT STRIP ──────────────────────────────────────────────── */
body.v2 .v2-verdict {
    display: flex; align-items: center; gap: 11px;
    margin-top: 22px;
    padding: 13px 16px;
    border-radius: 13px;
    font-size: 14.5px; font-weight: 700;
    border: 1px solid transparent;
}
body.v2 .v2-verdict.is-pass { background: var(--good-bg); border-color: rgba(61,220,151,0.3);  color: var(--good); }
body.v2 .v2-verdict.is-block{ background: var(--bad-bg);  border-color: rgba(255,107,107,0.3); color: var(--bad); }
body.v2 .v2-verdict.is-idle { background: rgba(255,255,255,0.05); border-color: var(--line); color: var(--t3); }
body.v2 .v2-verdict svg { flex-shrink: 0; }

/* ── ACTION BOX ─────────────────────────────────────────────────────
   Whatever the side column is showing this time: a prompt, a badge, or
   the two buttons. One box so every state is the same size and the
   card does not jump height between proposals.
   ------------------------------------------------------------------ */
body.v2 .v2-act { text-align: center; }
body.v2 .v2-act-ico {
    width: 46px; height: 46px;
    margin: 0 auto 14px;
    border-radius: 13px;
    display: flex; align-items: center; justify-content: center;
}
body.v2 .v2-act-label { display: block; font-size: 15px; font-weight: 700; color: #fff; margin-bottom: 8px; }
body.v2 .v2-act-sub   { display: block; font-size: 13px; line-height: 1.55; color: var(--t3); }
body.v2 .v2-act-big   { display: block; font-size: 22px; font-weight: 700; letter-spacing: -0.4px; margin-bottom: 6px; }
body.v2 .v2-act-big.is-pass { color: var(--good); }
body.v2 .v2-act-big.is-fail { color: var(--bad); }
body.v2 .v2-act .v2-btn,
body.v2 .v2-act .v2-btn-ghost { width: 100%; box-sizing: border-box; display: flex; }

/* ── VOTE BUTTONS ───────────────────────────────────────────────── */
body.v2 .v2-votes { display: flex; flex-direction: column; gap: 10px; }
body.v2 .v2-vote {
    box-sizing: border-box;
    display: flex; align-items: center; justify-content: center; gap: 9px;
    width: 100%;
    min-height: 48px;
    padding: 12px 18px;
    border-radius: 13px;
    font-family: 'Quicksand', sans-serif;
    font-size: 15px; font-weight: 700;
    cursor: pointer;
    border: 1px solid transparent;
    transition: transform 0.16s ease, filter 0.16s ease;
}
body.v2 .v2-vote:hover { transform: translateY(-1px); filter: brightness(1.08); }
body.v2 .v2-vote.is-yes { background: var(--good-bg); border-color: rgba(61,220,151,0.42);  color: var(--good); }
body.v2 .v2-vote.is-no  { background: var(--bad-bg);  border-color: rgba(255,107,107,0.42); color: var(--bad); }
body.v2 .v2-standing {
    display: flex; align-items: flex-start; gap: 8px;
    margin-top: 14px;
    font-size: 12.5px; line-height: 1.5;
    text-align: left;
    color: var(--t3);
}
body.v2 .v2-standing svg { flex-shrink: 0; margin-top: 1px; }

/* ── EMPTY STATE ────────────────────────────────────────────────── */
body.v2 .v2-empty {
    box-sizing: border-box;
    padding: 64px 30px;
    border: 1px dashed var(--line-strong);
    border-radius: 24px;
    text-align: center;
}
body.v2 .v2-empty h3 { margin: 16px 0 8px; font-size: 19px; font-weight: 700; color: #fff; }
body.v2 .v2-empty p  { margin: 0; font-size: 14.5px; color: var(--t3); }

/* ── BUY LAYOUT ─────────────────────────────────────────────────────
   The swap console and the things you read while deciding. Console
   first on a phone, because that is what the page is for.
   ------------------------------------------------------------------ */
body.v2 .v2-buy { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 20px; align-items: start; }
body.v2 .v2-buy-side { display: flex; flex-direction: column; gap: 20px; }
@media (max-width: 1100px) {
    body.v2 .v2-buy { grid-template-columns: minmax(0, 1fr); }
}

/* ── SWAP CONSOLE ───────────────────────────────────────────────────
   The tall min-height belongs to the widget and nothing else. It used
   to apply unconditionally, which wrapped a three-line pre-launch
   notice in 520px of empty box, most of a phone screen showing
   nothing. .is-live is added only when Jupiter actually mounts.
   ------------------------------------------------------------------ */
body.v2 .v2-console {
    box-sizing: border-box;
    border: 1px solid var(--line);
    border-radius: 24px;
    background: var(--surface);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    overflow: hidden;
    display: flex; flex-direction: column;
}
body.v2 .v2-console.is-live { min-height: 560px; }
body.v2 .v2-console-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 14px;
    padding: 16px 22px;
    border-bottom: 1px solid var(--line);
    background: rgba(255,255,255,0.025);
}
body.v2 .v2-console-title { font-size: 15px; font-weight: 700; color: #fff; }
body.v2 .v2-console-body { flex-grow: 1; display: flex; width: 100%; }
body.v2 .v2-console-msg {
    box-sizing: border-box;
    width: 100%;
    margin: auto;
    padding: 52px 30px;
    text-align: center;
}
body.v2 .v2-console-msg h4 { margin: 18px 0 10px; font-size: 19px; font-weight: 700; color: #fff; }
body.v2 .v2-console-msg p  { margin: 0 auto; max-width: 420px; font-size: 14.5px; line-height: 1.7; color: var(--t3); }
@media (max-width: 900px) {
    body.v2 .v2-console { border-radius: 20px; }
    body.v2 .v2-console-head { padding: 14px 18px; }
    body.v2 .v2-console-msg { padding: 38px 20px; }
}

/* ── CALCULATOR FIELD ───────────────────────────────────────────── */
body.v2 .v2-field {
    box-sizing: border-box;
    display: flex; align-items: center; gap: 12px;
    min-height: 66px;
    padding: 10px 16px;
    border: 1px solid var(--line-strong);
    border-radius: 14px;
    background: rgba(0,0,0,0.22);
    transition: border-color 0.18s ease, background 0.18s ease;
}
body.v2 .v2-field:focus-within { border-color: var(--accent); background: rgba(219,70,230,0.06); }
body.v2 .v2-field input {
    flex-grow: 1; min-width: 0;
    /* Fills the field's height rather than sitting centered at its own
       24px. The field looks like the target; without this only the
       middle 24px of it actually focuses the input, which on a phone
       means tapping the box does nothing.

       stretch alone gave 38px, because the field's own padding eats
       into it. min-height is what actually guarantees the 44, and the
       field's padding came down to 10px so the box does not grow. */
    align-self: stretch;
    min-height: 44px;
    background: transparent;
    border: none; outline: none; padding: 0;
    color: #fff;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 21px; font-weight: 700;
}
body.v2 .v2-field input::placeholder { color: var(--t4); }
body.v2 .v2-field input:disabled { color: var(--t2); }
/* Chrome and Safari put steppers on a number input that this layout
   has no room for, and that nobody wants on a currency field. */
body.v2 .v2-field input[type=number]::-webkit-outer-spin-button,
body.v2 .v2-field input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
body.v2 .v2-field input[type=number] { -moz-appearance: textfield; appearance: textfield; }
body.v2 .v2-field-unit {
    flex-shrink: 0;
    display: flex; align-items: center; gap: 9px;
    font-size: 14px; font-weight: 700; color: var(--accent-soft);
}
/* No border-radius. The circle crop was there to make a square
   third-party logo read as a token icon; the mark is a hexagon on
   transparency and a circle clips its points off. */
body.v2 .v2-field-unit img { width: 22px; height: 22px; display: block; }
body.v2 .v2-calc-note {
    margin: 0;
    font-size: 12.5px; line-height: 1.6;
    color: var(--t3);
    text-align: center;
}
body.v2 .v2-calc-arrow { display: flex; justify-content: center; color: var(--t4); }

/* ── ADDRESS BOX ────────────────────────────────────────────────── */
body.v2 .v2-addr {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 10px 10px 14px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: rgba(0,0,0,0.22);
}
body.v2 .v2-addr code {
    flex-grow: 1; min-width: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12.5px;
    color: var(--t2);
    word-break: break-all;
}
body.v2 .v2-copy {
    flex-shrink: 0;
    /* 44, not 40. Measured at 375px and it was the only control on the
       page under the minimum. */
    min-height: 44px;
    padding: 9px 15px;
    border-radius: 10px;
    border: 1px solid var(--line-strong);
    background: rgba(255,255,255,0.06);
    color: var(--t1);
    font-family: 'Quicksand', sans-serif;
    font-size: 12.5px; font-weight: 700;
    cursor: pointer;
}
body.v2 .v2-copy:hover { background: rgba(255,255,255,0.11); }

/* ── CODE BLOCK ─────────────────────────────────────────────────────
   One snippet on /developers. Scrolls sideways rather than wrapping,
   because a wrapped line of code is a line of code you have to read
   twice to be sure of.
   ------------------------------------------------------------------ */
body.v2 .v2-code { padding: 0; overflow: hidden; }
body.v2 .v2-code-head {
    display: flex; align-items: center; justify-content: space-between; gap: 14px;
    padding: 13px 18px;
    border-bottom: 1px solid var(--line);
    font-size: 12px; font-weight: 700; letter-spacing: 0.06em;
    color: var(--t3);
}
body.v2 .v2-code-head span:last-child { color: var(--t4); }
body.v2 .v2-code-body {
    margin: 0;
    padding: 18px;
    overflow-x: auto;
    background: rgba(0,0,0,0.26);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12.5px; line-height: 1.75;
    color: var(--t2);
    -webkit-text-size-adjust: 100%;
}
body.v2 .v2-code-body code { font: inherit; color: inherit; }
@media (max-width: 700px) {
    body.v2 .v2-code-body { font-size: 11.5px; padding: 14px; }
    body.v2 .v2-code-head { padding: 12px 14px; }
}

/* ── KEY/VALUE ROWS ─────────────────────────────────────────────────
   A label on the left, its answer on the right, separated by a rule.
   Wraps to two lines rather than squashing, because several of these
   hold a 44-character Solana address.
   ------------------------------------------------------------------ */
body.v2 .v2-rows { display: flex; flex-direction: column; }
body.v2 .v2-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 16px; flex-wrap: wrap;
    padding: 13px 0;
    border-bottom: 1px solid var(--line);
}
body.v2 .v2-row:first-child { padding-top: 0; }
body.v2 .v2-row:last-child { border-bottom: none; padding-bottom: 0; }
body.v2 .v2-row-k { font-size: 14px; color: var(--t3); }
body.v2 .v2-row-v { font-size: 14.5px; font-weight: 700; color: var(--t1); text-align: right; }
/* An address in a row is a link and needs a real touch target. */
body.v2 a.v2-row-v { display: inline-flex; align-items: center; min-height: 44px; }
@media (max-width: 900px) {
    body.v2 .v2-row { gap: 4px; }
    body.v2 .v2-row-v { text-align: left; }
}

/* ── NOTICE, DANGER VARIANT ─────────────────────────────────────── */
body.v2 .v2-notice.is-bad {
    border-color: rgba(255,107,107,0.32);
    background: var(--bad-bg);
    color: #ffc9c9;
    align-items: flex-start;
    padding: 20px 22px;
    font-size: 15px;
    line-height: 1.65;
}
body.v2 .v2-notice.is-bad strong { color: #fff; }
body.v2 .v2-notice.is-bad svg { margin-top: 3px; }

/* ── WALLET CARD ────────────────────────────────────────────────── */
body.v2 .v2-wallet-logo {
    width: 74px; height: 74px;
    margin: 0 auto 20px;
    padding: 12px;
    box-sizing: border-box;
    border-radius: 20px;
    border: 1px solid var(--line);
    background: rgba(255,255,255,0.04);
    display: block;
    object-fit: contain;
}
body.v2 .v2-wallet-where {
    display: flex; justify-content: center; flex-wrap: wrap;
    gap: 7px;
    margin: 18px 0 20px;
}
body.v2 .v2-wallet-where span {
    padding: 4px 10px;
    border-radius: 7px;
    background: rgba(255,255,255,0.06);
    font-size: 11.5px; font-weight: 700; letter-spacing: 0.5px;
    color: var(--t3);
}

/* ── PODIUM ─────────────────────────────────────────────────────────
   Three spots, the winner raised. On a phone they go in a row still,
   because a vertical podium is not a podium; only the sizes shrink.
   ------------------------------------------------------------------ */
body.v2 .v2-podium {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: end;
    gap: 14px;
    max-width: 720px;
    margin: 0 auto;
}
body.v2 .v2-spot {
    box-sizing: border-box;
    display: flex; flex-direction: column; align-items: center;
    padding: 24px 14px 20px;
    border: 1px solid var(--line);
    border-radius: 20px;
    background: var(--surface);
    text-decoration: none;
    text-align: center;
    min-width: 0;
    transition: transform 0.18s ease, border-color 0.18s ease;
}
body.v2 .v2-spot:hover { transform: translateY(-3px); }
body.v2 .v2-spot.is-1 { padding-top: 34px; padding-bottom: 28px; border-color: rgba(255,203,64,0.45); background: rgba(255,203,64,0.07); }
body.v2 .v2-spot.is-2 { border-color: rgba(214,214,224,0.34); }
body.v2 .v2-spot.is-3 { border-color: rgba(205,127,50,0.38); }
body.v2 .v2-spot-pos {
    font-size: 12px; font-weight: 800; letter-spacing: 1px;
    color: var(--t4);
    margin-bottom: 12px;
}
body.v2 .v2-spot.is-1 .v2-spot-pos { color: #ffcb40; }
body.v2 .v2-spot img {
    width: 62px; height: 62px;
    /* Rounded square, like the navbar avatar and the buttons. */
    border-radius: 18px;
    object-fit: cover;
    border: 2px solid var(--line-strong);
    margin-bottom: 12px;
}
body.v2 .v2-spot.is-1 img { width: 76px; height: 76px; border-color: #ffcb40; }
body.v2 .v2-spot-name {
    font-size: 14.5px; font-weight: 700; color: #fff;
    max-width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.v2 .v2-spot-xp { margin-top: 5px; font-size: 13.5px; font-weight: 700; color: var(--accent-soft); }
body.v2 .v2-spot-rank {
    margin-top: 11px;
    padding: 4px 10px;
    border-radius: 7px;
    background: rgba(255,255,255,0.07);
    font-size: 10.5px; font-weight: 800; letter-spacing: 0.8px;
    color: var(--t3);
}
@media (max-width: 900px) {
    body.v2 .v2-podium { gap: 7px; }
    body.v2 .v2-spot { padding: 16px 7px 14px; border-radius: 16px; }
    body.v2 .v2-spot.is-1 { padding-top: 22px; }
    body.v2 .v2-spot img { width: 46px; height: 46px; }
    body.v2 .v2-spot.is-1 img { width: 56px; height: 56px; }
    body.v2 .v2-spot-name { font-size: 12.5px; }
    body.v2 .v2-spot-xp { font-size: 12px; }
    body.v2 .v2-spot-rank { font-size: 9.5px; padding: 3px 7px; }
}

/* ── TABLE ──────────────────────────────────────────────────────────
   Scrolls sideways rather than squashing, because the alternative is
   a wallet address wrapped over four lines.
   ------------------------------------------------------------------ */
body.v2 .v2-table-wrap {
    border: 1px solid var(--line);
    border-radius: 20px;
    background: var(--surface);
    overflow: hidden;
}
body.v2 .v2-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
body.v2 .v2-table { width: 100%; border-collapse: collapse; min-width: 560px; }
body.v2 .v2-table th {
    padding: 15px 18px;
    text-align: left;
    font-size: 11.5px; font-weight: 800; letter-spacing: 1px;
    color: var(--t4);
    background: rgba(255,255,255,0.03);
    border-bottom: 1px solid var(--line);
    white-space: nowrap;
}
body.v2 .v2-table td {
    padding: 13px 18px;
    border-bottom: 1px solid rgba(255,255,255,0.055);
    font-size: 14.5px;
    color: var(--t2);
    vertical-align: middle;
}
body.v2 .v2-table tr:last-child td { border-bottom: none; }
body.v2 .v2-table tr.is-me td { background: rgba(219,70,230,0.09); }
body.v2 .v2-table-pos { font-weight: 700; color: var(--t3); white-space: nowrap; }
/* First three get a crown in its own metal, everyone else gets a
   number. v1 did this and the v2 rebuild lost it: a coloured number
   is not the same signal as a crown, and the top of a leaderboard is
   the one place worth a bit of ceremony. */
body.v2 .v2-crown { display: inline-flex; align-items: center; justify-content: center; }
body.v2 .v2-crown svg { display: block; }
body.v2 .v2-crown.is-1 { color: #ffd700; filter: drop-shadow(0 0 7px rgba(255,215,0,0.45)); }
body.v2 .v2-crown.is-2 { color: #cfd4da; filter: drop-shadow(0 0 6px rgba(207,212,218,0.32)); }
body.v2 .v2-crown.is-3 { color: #d98a4f; filter: drop-shadow(0 0 6px rgba(217,138,79,0.32)); }

/* ── BLOG LISTING ───────────────────────────────────────────────────
   A deliberate exception to "one component on every page". Every
   other page here is centred sections of panels, and a list of posts
   rendered that way reads as one more settings screen. A blog should
   look like a blog: a lead item with a real image, then a grid.

   Thumbnails have a fallback tile rather than an empty box, because
   most posts will not carry an image and a hole in the grid looks
   broken rather than sparse.
   ------------------------------------------------------------------ */
body.v2 .v2-thumb {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: linear-gradient(145deg, #2b0a2b 0%, #1b0719 60%, #160616 100%);
}
body.v2 .v2-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The fallback: the mark, small and quiet, on the tinted tile. */
body.v2 .v2-thumb.is-empty { display: flex; align-items: center; justify-content: center; }
body.v2 .v2-thumb.is-empty img {
    width: auto; height: 40%; max-width: 62%;
    /* Absolute ceiling as well as the percentage. On the lead tile the
       percentage is measured against a very wide box, and the mark
       ends up a billboard rather than a watermark. */
    max-height: 104px;
    object-fit: contain;
    /* 0.45, not the 0.3 the wordmark used. The wordmark was near enough
       monochrome and read fine that faint; the mark is saturated, and
       below about 0.4 it turns to a muddy smudge rather than a logo. */
    opacity: 0.45;
}
body.v2 .v2-thumb-tag {
    position: absolute; top: 12px; left: 12px;
    padding: 5px 10px;
    border-radius: 8px;
    background: rgba(8,3,14,0.72);
    backdrop-filter: blur(7px);
    -webkit-backdrop-filter: blur(7px);
    border: 1px solid rgba(255,255,255,0.14);
    font-size: 10.5px; font-weight: 800; letter-spacing: 1px;
    color: #fff;
}

/*
 * The listing gets a reading width of its own.
 *
 * .v2-section has no max-width, which is right for a grid of three
 * cards and wrong for a blog: on a wide monitor the lead card ran the
 * full width of the viewport and its image half alone was most of a
 * metre across. A blog is a reading surface whatever the screen is
 * doing.
 */
body.v2 .v2-lead,
body.v2 .v2-posts {
    max-width: 1080px;
    margin-left: auto;
    margin-right: auto;
}

body.v2 .v2-lead {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: 0;
    border: 1px solid var(--line);
    border-radius: 22px;
    overflow: hidden;
    background: var(--surface);
    text-decoration: none;
    transition: border-color .15s ease, transform .15s ease;
}
body.v2 .v2-lead:hover { border-color: rgba(219,70,230,0.45); transform: translateY(-2px); }
body.v2 .v2-lead .v2-thumb { aspect-ratio: auto; height: 100%; min-height: 250px; }
body.v2 .v2-lead-body { padding: 34px 34px 32px; display: flex; flex-direction: column; justify-content: center; }
body.v2 .v2-lead-body h2 {
    margin: 12px 0 10px;
    font-size: clamp(1.25rem, 2vw, 1.65rem);
    line-height: 1.22; font-weight: 700; letter-spacing: -0.6px;
    color: #fff;
}
body.v2 .v2-lead-body p { margin: 0; font-size: 15px; line-height: 1.65; color: var(--t3); }

body.v2 .v2-posts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
body.v2 .v2-post {
    display: flex; flex-direction: column;
    border: 1px solid var(--line);
    border-radius: 18px;
    overflow: hidden;
    background: var(--surface);
    text-decoration: none;
    transition: border-color .15s ease, transform .15s ease;
}
body.v2 .v2-post:hover { border-color: rgba(219,70,230,0.45); transform: translateY(-2px); }
body.v2 .v2-post-body { padding: 18px 20px 20px; flex: 1; display: flex; flex-direction: column; }
body.v2 .v2-post-body h3 {
    margin: 9px 0 8px;
    font-size: 16.5px; line-height: 1.32; font-weight: 700; color: #fff;
}
body.v2 .v2-post-body p { margin: 0; font-size: 14px; line-height: 1.6; color: var(--t3); }
body.v2 .v2-post-when { font-size: 12.5px; color: var(--t4); }

/* The homepage strip: same fallback, laid out as a row. */
/* .v2-panel brings padding: 34px 38px and this zeroed it, so the
   thumbnail sat against the card's own border on three sides and the
   text ran to the edge on the fourth: 1px of inset, all of it border.
   The panel's padding is too big for a row this size, so it is reset
   to a value that suits one rather than to nothing.

   The thumb's corner radius is the card's minus the padding, which is
   what keeps the two curves concentric. At 14px radius inside a 24px
   corner with no padding between them, they were fighting. */
body.v2 .v2-brief {
    display: flex; gap: 16px; align-items: stretch;
    padding: 14px; overflow: hidden;
    text-decoration: none;
}
body.v2 .v2-brief .v2-thumb {
    width: 148px; flex-shrink: 0; aspect-ratio: auto;
    border-radius: 10px;              /* 24 card - 14 padding */
}
body.v2 .v2-brief-body { padding: 2px 4px 2px 0; min-width: 0; flex: 1; display: flex; flex-direction: column; justify-content: center; }

@media (max-width: 900px) {
    body.v2 .v2-lead { grid-template-columns: minmax(0, 1fr); }
    body.v2 .v2-lead .v2-thumb { aspect-ratio: 16 / 9; min-height: 0; }
    body.v2 .v2-lead-body { padding: 24px 22px 26px; }
    body.v2 .v2-posts { grid-template-columns: minmax(0, 1fr); }
    body.v2 .v2-brief { gap: 12px; padding: 12px; }
    body.v2 .v2-brief .v2-thumb { width: 96px; border-radius: 8px; }   /* 20 - 12 */
}
body.v2 .v2-table-who { display: flex; align-items: center; gap: 12px; min-height: 44px; text-decoration: none; }
body.v2 .v2-table-who img { width: 34px; height: 34px; border-radius: 10px; object-fit: cover; flex-shrink: 0; border: 1px solid var(--line-strong); }
body.v2 .v2-table-name { display: block; font-size: 14.5px; font-weight: 700; color: #fff; }
body.v2 .v2-table-addr {
    display: block;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11.5px; color: var(--t4);
}
body.v2 .v2-table-xp { text-align: right; font-weight: 700; color: var(--t1); white-space: nowrap; }
body.v2 .v2-table-xp span { margin-left: 5px; font-size: 11px; font-weight: 700; color: var(--t4); }
@media (max-width: 900px) {
    body.v2 .v2-table th { padding: 12px 14px; }
    body.v2 .v2-table td { padding: 11px 14px; }
}

/* ── DASHBOARD LAYOUT ───────────────────────────────────────────── */
body.v2 .v2-dash { display: grid; grid-template-columns: 330px minmax(0, 1fr); gap: 20px; align-items: start; }
body.v2 .v2-dash-side { display: flex; flex-direction: column; gap: 20px; }
body.v2 .v2-dash-main { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
@media (max-width: 1100px) {
    body.v2 .v2-dash { grid-template-columns: minmax(0, 1fr); }
}

/* ── STICKY SIDE PANES ──────────────────────────────────────────────
   The dashboard's left column and the profile's right column both hold
   context you want to keep while reading the long column beside them:
   who you are and what to do next on one, the wallet and the badge
   case on the other. Both grids already carry align-items: start,
   which is what allows a child to be shorter than its row and
   therefore to stick at all.

   Gated on viewport HEIGHT as well as width. A sticky element taller
   than the space under the navbar pins its own top and puts its last
   panel permanently out of reach, which is worse than not sticking, so
   each pane sticks only on a window tall enough to hold it.

   Measured, by rendering each pane's real markup with its conditionals
   evaluated rather than approximated:

     profile, right column      692px
     dashboard, no badges yet   767px
     dashboard, full badge case 869px

   The gate is 880px of viewport for both, which is 92px of offset plus
   the taller everyday case. A heavy account on a window shorter than
   about 960px loses the last row of its badge case while pinned. That
   is the cheapest thing in either pane to lose, and it is why the
   badge panel sits last.

   (An earlier version of this comment claimed the dashboard pane was
   932px and gated it at 1060px, which meant it never stuck on a
   laptop. That figure came from a harness that rendered both branches
   of two if/else blocks. Measure the thing, then write the number.)

   No max-height with an inner scroll instead, deliberately. A pane
   that scrolls also clips, and both panes contain the badge case,
   whose hover tips are absolutely positioned outside their badge and
   would be cut off.

   Width gates match each grid's own collapse point. Below it the
   columns are one stack, and sticking half a stack to the top of the
   screen is nonsense. On the profile the column is display: contents
   down there in any case, which ignores position outright.
   ------------------------------------------------------------------ */
@media (min-width: 901px) and (min-height: 880px) {
    body.v2 .v2-prof-grid > .v2-prof-col:last-child {
        position: sticky;
        top: 92px;          /* 76px navbar, 16px of air */
    }
}
@media (min-width: 1101px) and (min-height: 880px) {
    body.v2 .v2-dash-side {
        position: sticky;
        top: 92px;
    }
}

/* ── WHO YOU ARE ────────────────────────────────────────────────── */
body.v2 .v2-id { text-align: center; }
body.v2 .v2-id-face {
    width: 92px; height: 92px;
    margin: 0 auto 16px;
    border-radius: 26px;
    object-fit: cover;
    border: 2px solid var(--line-strong);
    display: block;
}
body.v2 .v2-id-name { margin: 0 0 10px; font-size: 20px; font-weight: 700; color: #fff; word-break: break-word; }
body.v2 .v2-id-wallet {
    display: inline-block;
    margin-top: 12px;
    padding: 6px 12px;
    border-radius: 9px;
    border: 1px solid var(--line);
    background: rgba(0,0,0,0.22);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    color: var(--t2);
}

/* ── BADGES ─────────────────────────────────────────────────────── */
body.v2 .v2-badges { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
body.v2 .v2-badge {
    position: relative;
    width: 44px; height: 44px;
    border-radius: 12px;
    border: 1px solid var(--line);
    background: rgba(255,255,255,0.04);
    display: flex; align-items: center; justify-content: center;
    font-size: 19px;
    cursor: default;
}
body.v2 .v2-badge:hover { border-color: var(--badge-color, var(--accent)); }
body.v2 .v2-badge-tip {
    position: absolute; bottom: calc(100% + 9px); left: 50%;
    transform: translateX(-50%);
    z-index: 30;
    display: none;
    width: max-content; max-width: 190px;
    padding: 9px 12px;
    border-radius: 10px;
    border: 1px solid var(--line-strong);
    background: var(--bg-raised);
    box-shadow: 0 12px 30px rgba(0,0,0,0.5);
    text-align: center;
}
body.v2 .v2-badge:hover .v2-badge-tip,
body.v2 .v2-badge:focus-within .v2-badge-tip { display: block; }
body.v2 .v2-badge-tip b { display: block; font-size: 12.5px; color: #fff; }
body.v2 .v2-badge-tip span { display: block; margin-top: 3px; font-size: 11px; color: var(--t4); }

/* ── ACTION CARDS ───────────────────────────────────────────────── */
body.v2 .v2-acts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 700px) { body.v2 .v2-acts { grid-template-columns: minmax(0, 1fr); } }
body.v2 .v2-act-card {
    box-sizing: border-box;
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    padding: 20px 22px;
    border: 1px solid var(--line);
    border-radius: 18px;
    background: var(--surface);
    text-decoration: none;
    min-width: 0;
}
body.v2 .v2-act-card.is-ready { border-color: rgba(61,220,151,0.4); background: rgba(61,220,151,0.06); }
body.v2 .v2-act-card h3 { margin: 0 0 5px; font-size: 16px; font-weight: 700; color: #fff; }
body.v2 .v2-act-card p  { margin: 0; font-size: 13px; color: var(--t3); }
body.v2 .v2-act-card p.is-good { color: var(--good); font-weight: 700; }
body.v2 .v2-act-card .v2-btn { flex-shrink: 0; }
body.v2 .v2-act-done {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px; padding: 11px 18px;
    border-radius: 12px;
    border: 1px solid rgba(61,220,151,0.3);
    background: var(--good-bg);
    color: var(--good);
    font-family: 'Quicksand', sans-serif;
    font-size: 14px; font-weight: 700;
    cursor: default;
}

/* ── XP AND BALANCE CHART ───────────────────────────────────────────
   Plain SVG drawn in the markup: XP bars from xp_logs, a balance line
   from wallet_balance_daily. No charting library. JavaScript only
   switches which series is visible, so the chart still draws without
   it.

   It replaces a Chart.js line graph whose data was four hardcoded
   curves in dashboard.php scaled to the reader's net worth and
   labelled as their performance history. The line here is a row per
   day written by a cron that reads the chain, and it is not drawn at
   all until two of those rows exist.
   ------------------------------------------------------------------ */
body.v2 .v2-cc-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 14px; flex-wrap: wrap;
    margin-bottom: 16px;
}
body.v2 .v2-cc-head h3 { margin: 0 0 3px; font-size: 16px; font-weight: 700; color: #fff; }
body.v2 .v2-cc-head p { margin: 0; font-size: 13px; color: var(--t3); }

body.v2 .v2-cc-toggle {
    display: inline-flex;
    border: 1px solid var(--line-strong);
    border-radius: 11px;
    overflow: hidden;
    background: rgba(255,255,255,0.03);
}
body.v2 .v2-cc-btn {
    min-height: 36px;
    padding: 8px 13px;
    border: none;
    border-right: 1px solid var(--line);
    background: transparent;
    color: var(--t3);
    font-family: 'Quicksand', sans-serif;
    font-size: 12.5px; font-weight: 700;
    cursor: pointer;
}
body.v2 .v2-cc-btn:last-child { border-right: none; }
body.v2 .v2-cc-btn:hover { color: #fff; background: rgba(255,255,255,0.06); }
body.v2 .v2-cc-btn.is-on { background: rgba(219,70,230,0.16); color: #fff; }

/* The SVG stretches to the panel width at a fixed height, so the scale
   labels live out here in HTML where nothing can squash them. */
body.v2 .v2-cc { display: flex; align-items: stretch; gap: 10px; }
body.v2 .v2-cc-svg { flex: 1 1 auto; min-width: 0; height: 186px; }
body.v2 .v2-cc-yl,
body.v2 .v2-cc-yr {
    flex-shrink: 0;
    display: flex; flex-direction: column; justify-content: space-between;
    padding: 1px 0;
    font-size: 11px; font-weight: 700; color: var(--t4);
}
body.v2 .v2-cc-yl { text-align: right; }
body.v2 .v2-cc-yr { text-align: left; color: var(--good); }

/* One colour per series, which is what the toggle is separating:
   accent for the XP bars, green for the balance line. */
body.v2 .v2-cc-bar { fill: var(--accent); }
body.v2 .v2-cc-bar.is-zero { fill: rgba(255,255,255,0.07); }
body.v2 .v2-cc-line polyline { stroke: var(--good); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }

/* The toggle hides a series and its scale together. Showing a right
   hand axis for a line that is not drawn reads as a bug. */
body.v2 .v2-cc[data-show="xp"] .v2-cc-line,
body.v2 .v2-cc[data-show="xp"] .v2-cc-yr { display: none; }
body.v2 .v2-cc[data-show="dora"] .v2-cc-bars,
body.v2 .v2-cc[data-show="dora"] .v2-cc-yl { display: none; }

body.v2 .v2-cc-note {
    margin: 14px 0 0;
    font-size: 12.5px; line-height: 1.6; color: var(--t4);
}
body.v2 .v2-chart-axis {
    display: flex; justify-content: space-between;
    margin-top: 10px;
    font-size: 11.5px; color: var(--t4);
}

/* ── TRANSACTION LEDGER ─────────────────────────────────────────────
   The row markup comes from js/protocol-history.js, so the class
   names here are its, not this stylesheet's. Scoped under body.v2 so
   they beat global.css's v1 versions on specificity rather than on
   file order.
   ------------------------------------------------------------------ */
body.v2 .v2-ledger { padding: 0; overflow: hidden; }
body.v2 .v2-ledger-head {
    display: flex; align-items: center; justify-content: space-between; gap: 14px;
    padding: 18px 22px;
    border-bottom: 1px solid var(--line);
}
body.v2 .v2-ledger-head h3 { margin: 0; font-size: 16px; font-weight: 700; color: #fff; }
body.v2 .v2-ledger-head span { font-size: 11.5px; font-weight: 700; letter-spacing: 0.6px; color: var(--t4); }

body.v2 .ledger-row {
    display: grid;
    grid-template-columns: 38px minmax(0, 1fr) auto;
    align-items: center;
    gap: 14px;
    padding: 13px 22px;
    border-bottom: 1px solid rgba(255,255,255,0.055);
}
body.v2 .ledger-row:last-child { border-bottom: none; }
body.v2 .ledger-direction {
    width: 38px; height: 38px;
    border-radius: 11px;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255,255,255,0.05);
}
body.v2 .ledger-direction.out { background: var(--bad-bg); }
body.v2 .ledger-direction.in  { background: var(--good-bg); }
body.v2 .ledger-sig {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12.5px; color: var(--t2);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.v2 .ledger-meta { display: flex; gap: 12px; margin-top: 4px; font-size: 11.5px; color: var(--t4); }
body.v2 .ledger-type { font-weight: 700; letter-spacing: 0.4px; }
body.v2 .ledger-view-btn {
    display: inline-flex; align-items: center;
    min-height: 44px; padding: 10px 15px;
    border-radius: 10px;
    border: 1px solid var(--line-strong);
    font-size: 11.5px; font-weight: 700; letter-spacing: 0.6px;
    color: var(--t2); text-decoration: none;
}
body.v2 .ledger-view-btn:hover { background: rgba(255,255,255,0.07); color: #fff; }
body.v2 .ledger-empty { padding: 44px 22px; text-align: center; font-size: 14px; color: var(--t3); }
body.v2 .ledger-pagination { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; padding: 16px 22px; border-top: 1px solid var(--line); }
body.v2 .pg-btn {
    min-width: 44px; min-height: 44px;
    padding: 10px 13px;
    border-radius: 10px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--t3);
    font-family: 'Quicksand', sans-serif;
    font-size: 13.5px; font-weight: 700;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
}
/* The prev/next chevrons come from protocol-history.js without width
   or height attributes, so they need sizing here or they render at the
   SVG default. dashboard.php used to carry a MutationObserver that
   rewrote them after the fact; a CSS rule does the same job without
   watching the DOM. */
body.v2 .pg-btn svg { width: 15px; height: 15px; }
body.v2 .pg-btn:hover { background: rgba(255,255,255,0.07); color: #fff; }
body.v2 .pg-btn.active { border-color: var(--accent); background: rgba(219,70,230,0.14); color: #fff; }
body.v2 .pg-ellipsis { display: inline-flex; align-items: center; padding: 0 6px; color: var(--t4); }
@media (max-width: 900px) {
    body.v2 .ledger-row { padding: 12px 16px; gap: 11px; grid-template-columns: 34px minmax(0, 1fr); }
    body.v2 .ledger-direction { width: 34px; height: 34px; }
    /* The whole row is the link on a phone; protocol-history.js puts
       the click handler on it and this button would be a second, much
       smaller target for the same thing. */
    body.v2 .ledger-view-btn { display: none; }
    body.v2 .v2-ledger-head { padding: 15px 16px; }
}

@media (max-width: 700px) {
    body.v2 .v2-cc-svg { height: 150px; }
    body.v2 .v2-cc { gap: 8px; }
    body.v2 .v2-cc-yl, body.v2 .v2-cc-yr { font-size: 10.5px; }
    body.v2 .v2-cc-toggle { width: 100%; }
    body.v2 .v2-cc-btn { flex: 1 1 0; justify-content: center; }
}

/* ── MODAL ──────────────────────────────────────────────────────── */
body.v2 .v2-modal {
    position: fixed; inset: 0; z-index: 2000;
    display: none;
    align-items: flex-start; justify-content: center;
    padding: 24px 16px;
    overflow-y: auto;
    background: rgba(8,2,8,0.72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}
body.v2 .v2-modal.is-open { display: flex; }
body.v2 .v2-modal-card {
    box-sizing: border-box;
    width: 100%; max-width: 520px;
    margin: auto;
    border: 1px solid var(--line-strong);
    border-radius: 22px;
    background: var(--bg-raised);
    box-shadow: 0 30px 70px rgba(0,0,0,0.6);
    overflow: hidden;
}
body.v2 .v2-modal-head {
    display: flex; align-items: center; justify-content: space-between; gap: 14px;
    padding: 20px 22px;
    border-bottom: 1px solid var(--line);
}
body.v2 .v2-modal-head h2 { margin: 0; font-size: 19px; font-weight: 700; color: #fff; }
body.v2 .v2-modal-close {
    width: 44px; height: 44px;
    border-radius: 11px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--t3);
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}
body.v2 .v2-modal-close:hover { background: rgba(255,255,255,0.07); color: #fff; }
body.v2 .v2-modal-body { padding: 22px; }
body.v2 .v2-field-label { display: block; margin-bottom: 8px; font-size: 13px; font-weight: 700; color: var(--t2); }
body.v2 .v2-input {
    box-sizing: border-box;
    width: 100%;
    min-height: 46px;
    padding: 12px 14px;
    border-radius: 12px;
    border: 1px solid var(--line-strong);
    background: rgba(0,0,0,0.25);
    color: #fff;
    font-family: 'Quicksand', sans-serif;
    font-size: 15px;
}
body.v2 .v2-input:focus { outline: none; border-color: var(--accent); background: rgba(219,70,230,0.06); }
body.v2 .v2-input::placeholder { color: var(--t4); }
body.v2 textarea.v2-input { min-height: 88px; resize: vertical; line-height: 1.55; }
body.v2 .v2-form-row { margin-bottom: 18px; }
body.v2 .v2-social-row { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
body.v2 .v2-social-row svg { flex-shrink: 0; color: var(--t4); }
body.v2 .v2-upload {
    display: flex; align-items: center; gap: 15px;
    padding: 14px;
    margin-bottom: 20px;
    border: 1px dashed var(--line-strong);
    border-radius: 14px;
    cursor: pointer;
    position: relative;
}
body.v2 .v2-upload:hover { border-color: var(--accent); }
body.v2 .v2-upload img { width: 58px; height: 58px; border-radius: 16px; object-fit: cover; flex-shrink: 0; }
body.v2 .v2-upload b { display: block; font-size: 14.5px; color: #fff; }
body.v2 .v2-upload span { display: block; margin-top: 3px; font-size: 12.5px; color: var(--t4); }
body.v2 .v2-upload input[type=file] {
    position: absolute; inset: 0;
    opacity: 0; cursor: pointer; width: 100%; height: 100%;
}

/* ── STACKED BAR ────────────────────────────────────────────────────
   One bar, N segments, for showing how a whole is divided. .v2-split
   is the two-segment yes/no version; this one takes any number and
   pairs with a legend below it.
   ------------------------------------------------------------------ */
body.v2 .v2-stack {
    display: flex;
    height: 26px;
    border-radius: 13px;
    overflow: hidden;
    background: rgba(255,255,255,0.06);
    border: 1px solid var(--line);
}
body.v2 .v2-stack span { display: block; height: 100%; min-width: 2px; }
body.v2 .v2-legend { display: flex; flex-direction: column; margin-top: 20px; }
body.v2 .v2-legend-row {
    display: flex; align-items: center; gap: 14px;
    padding: 14px 0;
    border-bottom: 1px solid var(--line);
}
body.v2 .v2-legend-row:last-child { border-bottom: none; padding-bottom: 0; }
body.v2 .v2-legend-key { width: 12px; height: 12px; border-radius: 4px; flex-shrink: 0; }
body.v2 .v2-legend-name { flex-grow: 1; min-width: 0; }
body.v2 .v2-legend-name b { display: block; font-size: 15px; font-weight: 700; color: #fff; }
body.v2 .v2-legend-name span { display: block; margin-top: 2px; font-size: 13px; color: var(--t3); }
body.v2 .v2-legend-num { text-align: right; flex-shrink: 0; }
body.v2 .v2-legend-num b { display: block; font-size: 16px; font-weight: 700; color: #fff; }
body.v2 .v2-legend-num span { display: block; margin-top: 2px; font-size: 12.5px; color: var(--t4); }

/* ── FOOTER ─────────────────────────────────────────────────────── */
body.v2 .v2-footer {
    box-sizing: border-box;
    margin-top: 88px;
    padding: 56px var(--pad) 0;
    border-top: 1px solid var(--line);
}
body.v2 .v2-footer-top { display: flex; gap: 88px; padding-bottom: 44px; }
body.v2 .v2-footer-brand { width: 300px; flex-shrink: 0; }
body.v2 .v2-footer-brand img { height: 28px; width: auto; display: block; margin-bottom: 18px; }
body.v2 .v2-footer-brand p { margin: 0 0 22px; font-size: 14.5px; line-height: 1.7; color: var(--t3); }
body.v2 .v2-footer-cols { flex-grow: 1; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 30px; }
body.v2 .v2-footer-cols h4 { margin: 0 0 18px; font-size: 13px; font-weight: 700; letter-spacing: 0.9px; color: #fff; }
body.v2 .v2-footer-cols div { display: flex; flex-direction: column; gap: 12px; }
body.v2 .v2-footer-cols a { font-size: 14.5px; color: var(--t3); text-decoration: none; }
body.v2 .v2-footer-cols a:hover { color: #fff; }

body.v2 .v2-social { display: flex; gap: 10px; }
body.v2 .v2-social a {
    width: 44px; height: 44px;
    border-radius: 11px;
    border: 1px solid var(--line-strong);
    background: rgba(255,255,255,0.04);
    display: flex; align-items: center; justify-content: center;
}
body.v2 .v2-social a:hover { background: rgba(255,255,255,0.09); }

body.v2 .v2-footer-bottom {
    display: flex; align-items: center; justify-content: space-between;
    gap: 16px;
    padding: 22px 0 28px;
    border-top: 1px solid rgba(255,255,255,0.08);
}
body.v2 .v2-footer-bottom span { font-size: 13px; color: var(--t4); }
/* The avenuEmpire link in the copyright line.
   Without this it is painted by `body.v2 a { color: var(--accent-soft) }`
   at (0,1,1), which puts a bright pink word in the middle of a muted
   grey sentence. This selector is (0,2,2), so it wins, and `inherit`
   takes the line's own colour rather than naming it again.
   No underline, per Ken. The word is indistinguishable from the
   sentence at rest and the colour shift on hover is the only thing
   that marks it, which is the trade being made deliberately. */
body.v2 .v2-footer-bottom a {
    color: inherit;
    text-decoration: none;
}
/* :focus-visible carries the same weight as :hover here. With the
   underline gone, colour on hover is the whole affordance, and hover
   does not exist for somebody tabbing through the footer. */
body.v2 .v2-footer-bottom a:hover,
body.v2 .v2-footer-bottom a:focus-visible {
    color: var(--accent-soft);
}
body.v2 .v2-mint {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    color: var(--t2);
    padding: 6px 12px;
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: 9px;
    background: rgba(255,255,255,0.035);
    word-break: break-all;
}

@media (max-width: 900px) {
    /* 19px tall links are not a touch target. Padding rather than
       height so the text stays where it is optically, and the gap
       goes to zero because the padding now does the separating.

       13px, not 12px. 12 gave 43px against a 44px minimum, and it
       went unnoticed because the check that cleared this footer was
       run against the homepage with a height cutoff that stopped
       above it. A target that misses by one pixel misses. */
    body.v2 .v2-footer-cols div { gap: 0; }
    body.v2 .v2-footer-cols a { padding: 13px 0; }
    body.v2 .v2-footer { margin-top: 46px; padding-top: 32px; }
    body.v2 .v2-footer-top { flex-direction: column; gap: 26px; }
    body.v2 .v2-footer-brand { width: 100%; }
    body.v2 .v2-footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px 20px; }
    body.v2 .v2-footer-bottom { flex-direction: column; align-items: flex-start; }
}

/* ── LINKS ──────────────────────────────────────────────────────── */
body.v2 a { color: var(--accent-soft); }
body.v2 a:hover { color: #fff; }

/* ── MOTION ─────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    body.v2 *, body.v2 *::before, body.v2 *::after {
        animation-duration: 0.001ms !important;
        transition-duration: 0.001ms !important;
    }
    /* The blanket rule above collapses the duration, which for a
       keyframe that starts offset would freeze the hero art 8px below
       where it belongs rather than stopping it. Kill the animation
       outright and put the transform back to nothing. */
    body.v2 .v2-hero-art img {
        animation: none !important;
        transform: none !important;
    }
    /* Same trap, worse outcome. Collapsing the duration freezes each
       ripple on its 0% frame, which is scale(0.06) at opacity 0, so
       three invisible dots sit in the banner forever. Drop them and
       keep the dot field, which was always meant to be the resting
       state and reads as deliberate on its own. */
    body.v2 .v2-hero-ripple {
        animation: none !important;
        display: none;
    }
}

/* ── SKELETONS ──────────────────────────────────────────────────────
   A value that has not arrived yet shimmers rather than showing a
   zero or a dash that will change under the reader's eye. chain.js
   removes .v2-skel when it paints.

   Reduced-motion is handled by the global rule at the end of this
   file, which flattens the animation to nothing.
   ------------------------------------------------------------------ */
body.v2 .v2-skel {
    display: inline-block;
    min-width: 3.5em;
    border-radius: 6px;
    color: transparent !important;
    background: linear-gradient(
        90deg,
        rgba(255,255,255,0.06) 25%,
        rgba(255,255,255,0.13) 37%,
        rgba(255,255,255,0.06) 63%
    );
    background-size: 400% 100%;
    animation: v2-shimmer 1.4s ease infinite;
    user-select: none;
}
@keyframes v2-shimmer {
    0%   { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

/* A figure that is real but has not been refreshed lately. Dimmed,
   not hidden - it is still the best number available, and its tooltip
   says when it was read. */
body.v2 [data-stale="true"] { opacity: 0.72; }

/* ── LAZY SECTIONS ──────────────────────────────────────────────────
   Same idea as the v1 .lazy-section, but it fades and lifts rather
   than blanking the text, so nothing reflows when it resolves.
   ------------------------------------------------------------------ */
/* html.js is set by an inline script in header.php, in the head,
   before anything paints. Hiding is therefore conditional on
   JavaScript actually running: without it nothing is ever hidden, and
   with it the hide happens before the first paint rather than after,
   which is what made the top of the page flash.

   The .no-js rule this replaces never fired, because nothing on the
   site ever set that class. */
html.js body.v2 .v2-reveal {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 0.5s ease, transform 0.5s ease;
    will-change: opacity, transform;
}
html.js body.v2 .v2-reveal.is-in { opacity: 1; transform: none; }

/* ── MOBILE NAV OVERRIDES, LAST ON PURPOSE ──────────────────────────
   These were up in the navbar block, where they lost.
   `body.v2 .v2-signin { display: none }` and
   `body.v2 .v2-btn-ghost { display: inline-flex }` are both (0,2,0),
   so the one defined LATER won — and the buttons block sits below the
   navbar block. The result: "Sign in" stayed visible at 375px and
   pushed the logo out of the header.

   Third time this session a rule lost a cascade it looked like it
   should win. Fixed twice over: higher specificity (0,3,0) via
   .v2-nav, and placed at the end of the file so source order cannot
   betray it either.
   ------------------------------------------------------------------ */
@media (max-width: 900px) {
    body.v2 .v2-nav .v2-signin   { display: none; }
    body.v2 .v2-nav .v2-nav-links { display: none; }
    /* Room is tight at 375px: shrink the one button that stays. */
    body.v2 .v2-nav .v2-btn { padding: 10px 16px; font-size: 13.5px; }
    /* Tighter still once an avatar is in the row as well. A signed-out
       header keeps the roomier button above, because it has the space.

       header.php puts .is-signed-in on the container rather than this
       asking :has(.v2-user). PHP already knows the answer, and a
       layout that only fits on browsers with :has() is a layout that
       breaks on the ones without it, silently, at 375px. */
    body.v2 .v2-nav-actions.is-signed-in .v2-btn { padding: 10px 12px; font-size: 13px; }
    body.v2 .v2-nav-actions { gap: 9px; }
}

/* ── PROFILE ────────────────────────────────────────────────────────
   The one page here that is about a person rather than a subject, and
   it was built from .v2-page-head, the same component as /terms, with
   an avatar dropped in above the heading. Everything centred, so every
   signal said "document".

   A profile reads as a profile because of structure, not decoration:
   a banner, an avatar breaking its edge, and an identity block running
   along it rather than stacked under it. That is what this is.
   ------------------------------------------------------------------ */
/* The banner runs up behind the navbar, so only its lower part is
   ever seen. Both gradients below are written in percentages of the
   box for that reason: the geometry then holds at 375px and at
   1440px, and at whatever height the band is given.

   What this replaced had two problems on a phone. The base gradient
   ran at 120deg, which on a wide short box puts its brightest stop
   on a diagonal through the right hand side, and the fade to the
   page only began at 55% of the full height, below the part anyone
   can see. The result was a bright magenta block on the right ending
   in a hard horizontal line. The glow was worse: a fixed 640px box
   pinned to the top right, whose centre lands off to the LEFT once
   the banner is 375px wide, so on a phone it was not visible at all.
   Same class of bug as the fixed-radius seam on .v2-page-head. */
body.v2 .v2-prof-banner {
    position: relative;
    height: 200px;
    margin-top: -76px;      /* up behind the navbar, like the hero */
    overflow: hidden;
    background:
        linear-gradient(to bottom,
            rgba(22,6,22,0) 34%,
            rgba(22,6,22,0.70) 80%,
            var(--bg) 100%),
        radial-gradient(88% 165% at 72% 4%,
            rgba(219,70,230,0.46) 0%,
            rgba(219,70,230,0) 74%),
        linear-gradient(165deg, #1d0520 0%, #3a0a3c 46%, #560e58 100%);
}

body.v2 .v2-prof-head {
    position: relative; z-index: 2;
    margin-top: -74px;
    display: flex; align-items: flex-end; gap: 22px;
    padding: 0 var(--pad);
}
body.v2 .v2-prof-face {
    width: 132px; height: 132px;
    border-radius: 26px;          /* rounded square, never a circle */
    object-fit: cover;
    flex-shrink: 0;
    border: 3px solid var(--bg);
    background: var(--bg-raised);
    box-shadow: 0 16px 40px rgba(0,0,0,0.55);
}
body.v2 .v2-prof-id { flex: 1; min-width: 0; padding-bottom: 6px; }
body.v2 .v2-prof-id h1 {
    margin: 0 0 10px;
    font-size: clamp(1.6rem, 3vw, 2.3rem);
    line-height: 1.1; font-weight: 700; letter-spacing: -1.1px;
    color: #fff;
    overflow-wrap: anywhere;
}
body.v2 .v2-prof-chips { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
body.v2 .v2-prof-acts { display: flex; align-items: center; gap: 9px; padding-bottom: 8px; flex-shrink: 0; }
/* No :empty rule here. It was tried and it does nothing: the div is
   written across several lines in profile.php, so it holds a
   whitespace text node and :empty only matches an element with no
   children AND no text. The row is not rendered at all when there is
   nothing to put in it. See profile.php. */

body.v2 .v2-prof-bio {
    margin: 18px 0 0;
    max-width: 640px;
    font-size: 15px; line-height: 1.65; color: var(--t2);
}

/* Was an inline padding-top on the section in profile.php, which no
   media query could reach. */
body.v2 .v2-prof-body { padding-top: 20px; }

/* The stat rail: one row, divided, not four floating cards. */
body.v2 .v2-rail {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    margin-top: 28px;
    border: 1px solid var(--line-strong);
    border-radius: 18px;
    overflow: hidden;
    background: var(--surface);
}
body.v2 .v2-rail > div {
    padding: 17px 14px;
    text-align: center;
    border-right: 1px solid var(--line);
}
body.v2 .v2-rail > div:last-child { border-right: none; }
body.v2 .v2-rail b {
    display: block;
    font-size: 21px; font-weight: 700; color: #fff; line-height: 1.2;
    margin-bottom: 4px;
}
body.v2 .v2-rail span { font-size: 12px; color: var(--t4); }

body.v2 .v2-prof-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
    gap: 18px;
    align-items: start;
}
body.v2 .v2-prof-col { display: flex; flex-direction: column; gap: 18px; }

/* Activity: a real feed, with a rule down the left. */
body.v2 .v2-feed { display: flex; flex-direction: column; }
body.v2 .v2-feed-row {
    display: flex; gap: 14px; align-items: flex-start;
    padding: 13px 0;
    border-bottom: 1px solid var(--line);
}
body.v2 .v2-feed-row:last-child { border-bottom: none; padding-bottom: 0; }
body.v2 .v2-feed-row:first-child { padding-top: 0; }
body.v2 .v2-feed-ico {
    width: 34px; height: 34px; flex-shrink: 0;
    border-radius: 11px;
    display: flex; align-items: center; justify-content: center;
    font-size: 15px;
}
body.v2 .v2-feed-txt { flex: 1; min-width: 0; }
body.v2 .v2-feed-txt b { display: block; font-size: 14.5px; font-weight: 700; color: #fff; }
body.v2 .v2-feed-txt span { font-size: 12.5px; color: var(--t4); }
body.v2 .v2-feed-xp { font-size: 13.5px; font-weight: 700; color: var(--good); white-space: nowrap; }

/* The badge case: everything, with what is missing visible. */
body.v2 .v2-case { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 9px; }
body.v2 .v2-case-item {
    position: relative;
    aspect-ratio: 1;
    border-radius: 13px;
    border: 1px solid var(--line);
    background: rgba(255,255,255,0.03);
    display: flex; align-items: center; justify-content: center;
    font-size: 19px;
    cursor: default;
}
body.v2 .v2-case-item.is-locked { opacity: 0.26; filter: grayscale(1); }
body.v2 .v2-case-item:hover { border-color: var(--badge-color, var(--accent)); }
body.v2 .v2-case-item:hover .v2-badge-tip,
body.v2 .v2-case-item:focus-within .v2-badge-tip { display: block; }

@media (max-width: 900px) {
    body.v2 .v2-prof-banner { height: 150px; margin-top: -64px; }
    /* Centered on a phone. The avatar, the name, the chips and the
       actions all sit on one narrow column, and a left edge with
       nothing to its right reads as a mistake rather than a choice.
       align-items is the cross axis, which is horizontal once the
       container goes column. */
    body.v2 .v2-prof-head {
        margin-top: -56px;
        flex-direction: column; align-items: center; gap: 12px;
    }
    body.v2 .v2-prof-face { width: 96px; height: 96px; border-radius: 20px; }

    /* stretch, not the content width: a name long enough to wrap
       should center each line against the page, not against itself. */
    body.v2 .v2-prof-id { align-self: stretch; text-align: center; padding-bottom: 0; }
    body.v2 .v2-prof-chips { justify-content: center; }
    body.v2 .v2-prof-acts {
        align-self: stretch;
        justify-content: center; flex-wrap: wrap;
        padding-bottom: 0;
    }
    body.v2 .v2-prof-bio { text-align: center; margin: 10px auto 0; }

    /* The gap under the header was 68px of stacked margins with no
       bio to fill it: 6px of id padding, 14px for the empty actions
       row, 20px of section padding and 28px above the rail. */
    body.v2 .v2-prof-body { padding-top: 12px; }
    body.v2 .v2-prof-body .v2-rail { margin-top: 18px; }
    body.v2 .v2-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    body.v2 .v2-rail > div:nth-child(2n) { border-right: none; }
    body.v2 .v2-rail > div { border-bottom: 1px solid var(--line); }
    body.v2 .v2-rail > div:last-child { grid-column: 1 / -1; border-bottom: none; }
    body.v2 .v2-prof-grid { grid-template-columns: minmax(0, 1fr); }
    body.v2 .v2-case { grid-template-columns: repeat(5, minmax(0, 1fr)); }
    /* On one column the two wrappers would keep their panels grouped,
       which buries the wallet and the badge case under a long ledger.
       display:contents promotes all four panels to grid children so
       order can interleave them. The grid's own gap is the same 18px
       the wrappers were applying, so nothing moves apart from order. */
    body.v2 .v2-prof-col { display: contents; }
    body.v2 .v2-ord1 { order: 1; }
    body.v2 .v2-ord2 { order: 2; }
    body.v2 .v2-ord3 { order: 3; }
    body.v2 .v2-ord4 { order: 4; }
}

/* ══════════════════════════════════════════════════════════════
   XP ELSEWHERE — the sidebar card that brings XP over from a
   sister site. dashboard.php fills it in after the page paints.

   Mobile first: one column, the button full width under the
   figure, because at 375px a side-by-side amount and button
   leaves the button too narrow to read and too narrow to hit.
   ══════════════════════════════════════════════════════════════ */
body.v2 .v2-xpe {
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface);
    padding: 13px 14px;
}
body.v2 .v2-xpe + .v2-xpe { margin-top: 10px; }

body.v2 .v2-xpe-site {
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--t3);
    margin: 0 0 4px;
}
body.v2 .v2-xpe-amt {
    font-size: 22px;
    font-weight: 700;
    line-height: 1.15;
    color: #fff;
    margin: 0 0 10px;
}
body.v2 .v2-xpe-amt span { font-size: 13px; font-weight: 600; color: var(--t3); margin-left: 4px; }

/* Amount field and the All shortcut, side by side above the button. */
body.v2 .v2-xpe-row {
    display: flex;
    gap: 8px;
    align-items: stretch;
    margin: 0 0 10px;
}
body.v2 .v2-xpe-row .v2-xpe-in {
    min-height: 44px;
    padding: 10px 12px;
    font-size: 15px;
    text-align: right;
}
/* No native spinners. At this width the arrows eat the digits. */
body.v2 .v2-xpe-in::-webkit-outer-spin-button,
body.v2 .v2-xpe-in::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
body.v2 .v2-xpe-in { -moz-appearance: textfield; appearance: textfield; }

body.v2 .v2-xpe-max {
    flex: 0 0 auto;
    min-height: 44px;
    padding: 0 14px;
    border-radius: 12px;
    border: 1px solid var(--line-strong);
    background: rgba(255,255,255,0.06);
    color: var(--t1);
    font-family: 'Quicksand', sans-serif;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}
body.v2 .v2-xpe-max:hover,
body.v2 .v2-xpe-max:focus-visible { background: rgba(255,255,255,0.11); }

body.v2 .v2-xpe-note {
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--t3);
    margin: 0;
}
body.v2 .v2-xpe-note.is-warn { color: var(--warn); }

/* The button sits flush to the card edges on a phone. */
body.v2 .v2-xpe .v2-btn { min-height: 44px; font-size: 14px; }

/* Confirmation dialog for bringing XP over. Uses the shared .v2-modal
   shell; these are just the two blocks inside its body. */
body.v2 .v2-xpe-c-rows {
    border: 1px solid var(--line);
    border-radius: 12px;
    padding: 4px 16px;
    background: var(--surface);
}
/*
 * .v2-row zeroes its own outer padding so a list sits flush inside a
 * panel. In a bordered box that collapses the first and last rows
 * against the border and the whole thing reads as cramped, so put it
 * back. 4px on the box plus 13px on the row is the same breathing
 * room the rows already have between them.
 */
body.v2 .v2-xpe-c-rows .v2-row:first-child { padding-top: 13px; }
body.v2 .v2-xpe-c-rows .v2-row:last-child {
    padding-bottom: 13px;
    border-bottom: none;
}

body.v2 .v2-xpe-c-actions { display: flex; gap: 10px; }
body.v2 .v2-xpe-c-actions button { flex: 1 1 0; min-height: 48px; }


/*
 * <picture> is a wrapper for source selection, not a layout box. Every
 * rule on this site targets the img, and .v2-hero-art is a flex
 * container whose item should be the img rather than a shrink-wrapping
 * inline element around it. display:contents takes the wrapper out of
 * layout entirely, so adding AVIF and WebP sources changed no geometry
 * anywhere.
 */
body.v2 picture { display: contents; }

/* ══════════════════════════════════════════════════════════════
   HELP CENTER

   Mobile first throughout: one column, 44px targets, and the
   category grid only becomes a grid when there is room for one.
   ══════════════════════════════════════════════════════════════ */
body.v2 .v2-help-wrap { max-width: 1000px; margin: 0 auto; }

body.v2 .v2-crumbs {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    margin: 0 0 16px;
    font-size: 13px; color: var(--t3);
}
body.v2 .v2-crumbs a { color: var(--t3); text-decoration: none; }
body.v2 .v2-crumbs a:hover, body.v2 .v2-crumbs a:focus-visible { color: var(--accent-soft); }
body.v2 .v2-crumbs [aria-current] { color: var(--t1); font-weight: 600; }

body.v2 .v2-help-h1 {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    margin: 0 0 12px;
}
body.v2 .v2-help-h1-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 46px; height: 46px; flex-shrink: 0;
    border-radius: 13px;
    background: rgba(219,70,230,0.14);
    color: var(--accent-soft);
}

/* ── SEARCH ───────────────────────────────────────────────────── */
body.v2 .v2-help-search {
    display: flex; gap: 9px; align-items: stretch;
    width: 100%; max-width: 620px;
    margin: 26px auto 0;
    position: relative;
}
body.v2 .v2-help-search-ico {
    position: absolute; left: 15px; top: 50%; transform: translateY(-50%);
    display: flex; color: var(--t4); pointer-events: none;
}
body.v2 .v2-help-search input {
    box-sizing: border-box;
    flex: 1 1 auto; min-width: 0;
    min-height: 50px;
    padding: 12px 16px 12px 44px;
    border-radius: 14px;
    border: 1px solid var(--line-strong);
    background: rgba(0,0,0,0.3);
    color: #fff;
    font-family: 'Quicksand', sans-serif;
    font-size: 15px;
}
body.v2 .v2-help-search input:focus {
    outline: none; border-color: var(--accent); background: rgba(219,70,230,0.06);
}
body.v2 .v2-help-search input::placeholder { color: var(--t4); }
body.v2 .v2-help-search .v2-btn { flex: 0 0 auto; min-height: 50px; }

body.v2 .v2-help-eyebrow {
    margin: 0 0 16px;
    font-size: 12px; font-weight: 800; letter-spacing: 1.8px;
    text-transform: uppercase; color: var(--t3);
}

/* ── CATEGORY GRID ────────────────────────────────────────────── */
body.v2 .v2-help-cats { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
@media (min-width: 720px)  { body.v2 .v2-help-cats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1040px) { body.v2 .v2-help-cats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

body.v2 .v2-help-cat {
    display: flex; gap: 14px; align-items: flex-start;
    padding: 18px;
    border: 1px solid var(--line);
    border-radius: 16px;
    background: var(--surface);
    text-decoration: none;
    transition: border-color .15s ease, transform .15s ease, background .15s ease;
}
body.v2 .v2-help-cat:hover, body.v2 .v2-help-cat:focus-visible {
    border-color: rgba(219,70,230,0.45);
    background: var(--surface-2);
    transform: translateY(-2px);
}
body.v2 .v2-help-cat-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; flex-shrink: 0;
    border-radius: 11px;
    background: rgba(219,70,230,0.13);
    color: var(--accent-soft);
}
body.v2 .v2-help-cat-body { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
body.v2 .v2-help-cat-name { font-size: 15.5px; font-weight: 700; color: #fff; }
body.v2 .v2-help-cat-desc { font-size: 13px; line-height: 1.55; color: var(--t3); }
body.v2 .v2-help-cat-n    { font-size: 12px; font-weight: 700; color: var(--t4); }

/* ── ARTICLE ROWS ─────────────────────────────────────────────── */
body.v2 .v2-help-list { display: flex; flex-direction: column; gap: 10px; }
body.v2 .v2-help-row {
    display: flex; gap: 13px; align-items: center;
    min-height: 56px;
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-radius: 14px;
    background: var(--surface);
    text-decoration: none;
    transition: border-color .15s ease, background .15s ease;
}
body.v2 .v2-help-row:hover, body.v2 .v2-help-row:focus-visible {
    border-color: rgba(219,70,230,0.45); background: var(--surface-2);
}
body.v2 .v2-help-row-ico {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; flex-shrink: 0;
    border-radius: 9px;
    background: rgba(219,70,230,0.11);
    color: var(--accent-soft);
}
body.v2 .v2-help-row-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 auto; }
body.v2 .v2-help-row-title { font-size: 14.5px; font-weight: 700; color: var(--t1); line-height: 1.35; }
body.v2 .v2-help-row-sub   { font-size: 12.5px; line-height: 1.5; color: var(--t3); }
body.v2 .v2-help-row-cat   { font-size: 11.5px; font-weight: 700; letter-spacing: 0.4px; color: var(--t4); }
body.v2 .v2-help-row-meta {
    display: none;                       /* no room for it on a phone */
    align-items: center; gap: 5px; flex-shrink: 0;
    font-size: 12.5px; color: var(--t4); font-variant-numeric: tabular-nums;
}
body.v2 .v2-help-row-chev { display: flex; flex-shrink: 0; color: var(--t4); }
@media (min-width: 560px) { body.v2 .v2-help-row-meta { display: inline-flex; } }

/* ── ARTICLE PAGE ─────────────────────────────────────────────── */
body.v2 .v2-help-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
@media (min-width: 960px) {
    body.v2 .v2-help-grid { grid-template-columns: minmax(0, 1fr) 300px; align-items: start; }
}
body.v2 .v2-help-article { padding: 26px 22px 30px; }
@media (min-width: 720px) { body.v2 .v2-help-article { padding: 34px 34px 38px; } }

body.v2 .v2-help-kicker {
    display: flex; align-items: center; gap: 7px;
    margin: 0 0 12px;
    font-size: 11.5px; font-weight: 800; letter-spacing: 1.4px;
    color: var(--accent-soft);
}
body.v2 .v2-help-article h1 {
    margin: 0 0 12px;
    font-size: clamp(1.5rem, 4.2vw, 2.05rem);
    line-height: 1.2; letter-spacing: -0.7px; font-weight: 700; color: #fff;
}
body.v2 .v2-help-meta {
    display: flex; flex-wrap: wrap; gap: 16px;
    margin: 0 0 16px;
    font-size: 12.5px; color: var(--t4);
}
body.v2 .v2-help-meta span { display: inline-flex; align-items: center; gap: 6px; }

body.v2 .v2-help-tags { display: flex; flex-wrap: wrap; gap: 7px; margin: 0 0 22px; }
body.v2 .v2-help-tag {
    padding: 4px 10px;
    border-radius: 999px;
    background: rgba(255,255,255,0.06);
    border: 1px solid var(--line);
    font-size: 11.5px; font-weight: 600; color: var(--t3);
}

body.v2 .v2-help-body { font-size: 15px; }
body.v2 .v2-help-body h2 { margin: 28px 0 10px; font-size: 1.2rem; font-weight: 700; color: #fff; }
body.v2 .v2-help-body h3 { margin: 22px 0 8px;  font-size: 1.05rem; font-weight: 700; color: #fff; }
body.v2 .v2-help-body p  { margin: 0 0 14px; }
body.v2 .v2-help-body ul, body.v2 .v2-help-body ol { margin: 0 0 14px; padding-left: 22px; }
body.v2 .v2-help-body li { margin-bottom: 7px; }
body.v2 .v2-help-body a  { color: var(--accent-soft); }
body.v2 .v2-help-body a:not([href]) { color: inherit; text-decoration: none; }
body.v2 .v2-help-body blockquote {
    margin: 0 0 14px; padding: 2px 0 2px 16px;
    border-left: 3px solid var(--line);
    color: var(--t3); font-style: normal;
}
body.v2 .v2-help-body blockquote > *:last-child { margin-bottom: 0; }
body.v2 .v2-help-body hr { margin: 24px 0; border: 0; border-top: 1px solid var(--line); }
body.v2 .v2-help-body img { max-width: 100%; height: auto; border-radius: 10px; }
body.v2 .v2-help-body code {
    padding: 2px 6px; border-radius: 6px;
    background: rgba(255,255,255,.06);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px;
}
body.v2 .v2-help-body pre {
    margin: 0 0 14px; padding: 14px; border-radius: 12px;
    background: rgba(255,255,255,.05); border: 1px solid var(--line);
    overflow-x: auto; font-size: 13px; line-height: 1.6;
}
body.v2 .v2-help-body pre code { padding: 0; background: none; font-size: inherit; }
/* A wide table scrolls on its own rather than pushing the page sideways. */
body.v2 .v2-help-body table {
    display: block; width: 100%; overflow-x: auto;
    margin: 0 0 14px; border-collapse: collapse; font-size: 14px;
}
body.v2 .v2-help-body th, body.v2 .v2-help-body td {
    padding: 9px 12px; text-align: left; vertical-align: top;
    border-bottom: 1px solid var(--line); white-space: nowrap;
}
body.v2 .v2-help-body th { color: #fff; font-weight: 700; }
body.v2 .v2-help-body tr:last-child td { border-bottom: 0; }
body.v2 .v2-help-body > *:last-child { margin-bottom: 0; }

body.v2 .v2-help-side { display: flex; flex-direction: column; gap: 16px; }
body.v2 .v2-help-rel { display: flex; flex-direction: column; gap: 2px; }
body.v2 .v2-help-rel a {
    display: flex; gap: 10px; align-items: flex-start;
    min-height: 44px; padding: 9px 0;
    font-size: 13.5px; line-height: 1.45;
    color: var(--t2); text-decoration: none;
}
body.v2 .v2-help-rel a svg { flex-shrink: 0; margin-top: 2px; color: var(--accent-soft); }
body.v2 .v2-help-rel a:hover, body.v2 .v2-help-rel a:focus-visible { color: #fff; }
