/* ============================================================
   PUBLIC SITE — Panel V2 token layer + PUBLIC DESIGN SYSTEM (STAFF ONLY).

   Linked only from header.tpl's {if $hdrPubV2} gate, which is only ever
   true for a verified staff session on an allowlisted public route
   (includes/public-v2-gate.inc.php). A customer, a logged-out visitor
   and a crawler never download this file and never see this class.

   Three jobs:
     1. carry the --pv2-* palette onto public pages. header-v2-preview.css
        deliberately writes hex because the tokens are absent out here, but
        panel-sitenav.css — the shared #c2sitenav rail — is written against
        the tokens, so they have to exist for the rail to render correctly.
     2. the handful of public-page corrections the panel chrome needs.
     3. THE PUBLIC DESIGN SYSTEM (.pv2p-*) every redesigned marketing page
        is built from. See "PART 2" below and design-contract.md.

   The public site's layout.css is NOT edited. Everything here is additive
   and scoped under body.pubv2, exactly the separation panel-sitenav.css
   already keeps.

   SCOPING RULE THAT MAKES THIS SAFE: body.pubv2 is on EVERY allowlisted
   public route the moment a staff member is signed in — including pages
   nobody has redesigned yet. So this file contains NO bare-element rule
   under body.pubv2 alone. Everything in PART 2 is scoped through an
   opt-in class (.pv2p / .pv2p-band / .pv2p-*), so an unconverted page is
   byte-identical to what it was before.

   Design contract: CLAUDE-DESIGN-SYSTEM.md +
   ~/survivalservers/goals/2026-08-04-public-v2-redesign/design-contract.md.
   Values mirror css/sspanel/panel-v2-core.css — one palette, not a second
   copy with drift.
   ============================================================ */

/* ============================================================
   PART 1 — tokens + shared chrome (unchanged behaviour)
   ============================================================ */

body.pubv2 {
	--pv2-surface: #1a1a1b;      /* deep chrome: toolbars, card headers */
	--pv2-panel: #212122;        /* card body */
	--pv2-raised: #252526;       /* raised sections */
	--pv2-field: #2d2d2e;        /* inputs, wells, chips */
	--pv2-hover: #333333;
	--pv2-border: #3a3a3b;
	--pv2-border-strong: #4c4c4d;
	--pv2-line: #2c2c2d;
	--pv2-text: #ffffff;
	--pv2-text-soft: #cccccc;
	--pv2-text-muted: #999999;
	--pv2-text-dim: #666666;
	--pv2-accent: #d5a84a;
	--pv2-gold: #eaa824;
	--pv2-gold-deep: #db9b15;
	--pv2-ok: #64d977;
	--pv2-ok-bg: rgba(100, 217, 119, 0.10);
	--pv2-warn: #f0ad4e;
	--pv2-warn-bg: rgba(240, 173, 78, 0.10);
	--pv2-danger: #f25757;
	--pv2-danger-bg: rgba(242, 87, 87, 0.10);
	--pv2-info: #5bc0de;
	--pv2-info-bg: rgba(91, 192, 222, 0.10);
	--pv2-radius: 4px;
	--pv2-radius-lg: 6px;
	--pv2-mono: Consolas, 'Courier New', Monaco, monospace;
	--pv2-ctl-h: 32px;
}

/* ---------- the account door ----------
   The panels suppress the brand-row chip because their own submenu carries
   the account cell. A public page has no submenu, so the chip is the only
   account door here and has to come back. */
body.pubv2.v2hdr.v2hdr-panel #c2brand .c2chip { display: inline-block !important; }

/* ---------- the brand row gets the same rail the panels give it ----------
   The rail ellipsises its own labels rather than overflow the page, which is
   correct — but on a public page it was doing it at 1920, because the public
   .container is bootstrap's FIXED 1170px while both panels widen theirs above
   1400 (the design contract's one content rail). Same rule, same number, so the
   header lines up with the page band underneath it instead of sitting inside a
   narrower box. Scoped to #c2brand: each page still owns its own body rail. */
@media (min-width: 1400px) {
	body.pubv2 #c2brand .container {
		width: 100%;
		/* --pv2p-rail-wide is declared with the rail variants in PART 2. It is
		   a token and not a literal because the header rail and the page rail
		   have to be the SAME number: Ryan's first note on the game-page
		   preview was that the chrome widened to 1560 and the body stayed at
		   bootstrap's 1170, which reads as a half-converted page. Two copies
		   of one number is how that comes back. */
		max-width: var(--pv2p-rail-wide);
	}
}

/* The public brand row carries an account door the panel rows do not, so its
   right-hand group is ~150px heavier and the rail runs out of room a breakpoint
   earlier. The socials go first — they are decoration, they are in the footer,
   and panel-sitenav.css already drops them at 1199 for the same reason. The rail
   needs ~766px for six full labels; below 1600 the row cannot pay for both. */
@media (max-width: 1599px) {
	body.pubv2 #c2brand .c2rgt .c2socials { display: none !important; }
}

/* The short-label step USED TO BE HERE, at 991-1399, and it was right — but
   scoped body.pubv2 it only ever reached the public site, so the SAME rail
   inside the two panels kept its full labels and ellipsised them instead
   ("Game Server...", "Location..." measured at 1280 and 1366 on the customer
   panel, 2026-08-04). It now lives with the rail it belongs to, in
   css/sspanel/panel-sitenav.css's 1399 step, which all three surfaces load.
   Same numbers, same result out here — one rail, one behaviour.

   The logo step below stays: it pays for the account door that only the
   public brand row carries. */
@media (min-width: 991px) and (max-width: 1199px) {
	body.pubv2 #c2brand .c2logo img { height: 26px; }
}

/* ---------- the rail is the nav ----------
   #topBar and #topNav are already suppressed by the panel-chrome rules the
   v2hdr-panel class brings with it, which leaves the classic bar's own
   bottom margin behind on templates that lean on it. Close the gap so the
   page band starts flush under the rail, the way it does on both panels. */
body.pubv2 #header { margin-bottom: 0; }
body.pubv2 #header + section.page-title,
body.pubv2 #header + section { margin-top: 0; }

/* ---------- the footer joins the rail ----------
   *** MOVED OUT, 2026-08-04. Now css/public-v2-footer.css §10. ***

   The rule was `body.pubv2 #wrapper #footer .container` at >=1400, plus
   the >=1710 no-container-query fallback for the game list's third
   column. Both were correct and both were unreachable where they were
   needed most: THIS FILE IS LINKED ONLY FROM header.tpl's {if $hdrPubV2}
   gate, so it exists on public pages and nowhere else. The customer panel
   and the Death Star include the same footer.tpl and got neither.

   Everything that skins the footer now lives in the one sheet footer.tpl
   links for itself, scoped to the footer element's own class rather than
   to a body class, so all three surfaces get the identical footer. Do not
   re-add a footer rule here — a rule in this file can only ever reach one
   of the three surfaces, which is the exact bug that split the footer in
   two. */

/* ============================================================
   ============================================================
   PART 2 — THE PUBLIC DESIGN SYSTEM  (.pv2p-*)
   ============================================================

   Derived, not invented. Three sources, in this order of authority:
     - css/sspanel/panel-v2-core.css  the token set + component vocabulary
     - css/about-v2.css               the one public page Ryan approved
     - CLAUDE-DESIGN-SYSTEM.md        the fit/finish contract

   HOW A PAGE OPTS IN — two classes on markup that already exists, and
   nothing else. There is no body class to add and no per-page stylesheet
   required:

     <section class="page-title pv2p-band pv2p-w-read">   the title band
     <section class="pv2p pv2p-w-read">                   the content shell

   Both take the SAME width variant so the band title and the content
   below it share one left edge. Inside .pv2p, put components.

   THREE RULES THAT ARE NOT NEGOTIABLE
   1. Hierarchy comes from COMPONENTS, never from changing a heading
      level. Every public page's heading outline is SEO content and is
      frozen. .pv2p-chead styles whatever heading tag it is handed, so a
      section that must stay an <h4> can still read as a section head.
   2. Any <a>-based component must be written a.pv2p-btn / a.pv2p-chip /
      a.pv2p-card. `body.pubv2 .pv2p a` is (0,3,1) and repaints a bare
      (0,2,1) component gold-on-gold — the exact trap about-v2.css hit.
   3. The public site loads css/fa-subset.min.css, NOT full Font Awesome.
      An icon outside the subset renders as literally nothing. Every glyph
      this file draws is CSS geometry (borders/masks), so the system has
      zero font dependency. Check the subset before adding an <i>.

   FIVE MORE, FROM THE ORDER-FLOW AUDIT (2026-08-04) — NON-NEGOTIABLE
   The order page runs ~800 lines of inline JS on 100% of its traffic that
   pulls the legacy Bootstrap form apart and rebuilds it as the checkout
   console. It matches nodes by tag, by class, by DOM position and by
   regex on heading text. Any failed assumption calls rollback() and the
   visitor silently gets the 2013 form — no PHP error, no console error,
   nothing in any log. Revenue degrades with no alarm. These five exist so
   a SHARED sheet can never reach into that page through a class it
   happens to share:

   4. NEVER restyle a bare Bootstrap structural class. .panel,
      .panel-body, .panel-title, .row, .col-md-* are the JS's hooks and
      they are on every page on the site. Every rule here is scoped
      body.pubv2 PLUS a component class we added — including the two
      places this file does touch bootstrap markup (`.pv2p > .container`
      for the rail, and the `.pv2p-split` grid neutralisers), both of
      which only ever match inside a section a page opted in.
   5. RENAME NOTHING. A page ADDS .pv2p-* beside the classes that are
      already there; it never swaps one for the other. Any component that
      needs a rename to work is the wrong component.
   6. NEVER put transform / filter / contain / a new stacking context on a
      wrapper that could sit above a payment form. The codebase already
      carries a parallax `transform` that trapped the Stripe modal below
      the footer. This is why .callout's bloom is a layered BACKGROUND and
      not an absolutely-positioned ::before with `> * { z-index: 1 }`.
   7. `visibility: hidden` is load-bearing — JS reads .css('visibility')
      to decide a grid layout. Never "tidy" one into `display: none`.
      Equally: never set `display` on .toggle-content (the accordion's
      jQuery owns it inline).
   8. There is a global `.alert-success { display: none !important }` on
      the order page. Do NOT build a success/confirmation component on
      that class name — the public note component is .pv2p-note.
   ============================================================ */

body.pubv2 {
	/* --- vertical rhythm. One scale, used everywhere. --- */
	--pv2p-s1: 6px;
	--pv2p-s2: 10px;
	--pv2p-s3: 14px;
	--pv2p-s4: 18px;   /* card padding, list gaps */
	--pv2p-s5: 26px;   /* between cards */
	--pv2p-s6: 36px;   /* between sections */
	--pv2p-s7: 52px;   /* shell top/bottom */

	/* --- type. Same ramp about-v2.css was approved on: 14px/1.62 body,
	   growing to 15.5px/1.66 once the rail widens at 1400 (character
	   count is what matters, so the box and the type grow together). --- */
	--pv2p-font: 'Open Sans', Arial, sans-serif;
	--pv2p-fs-body: 14px;
	--pv2p-lh-body: 1.62;
	--pv2p-fs-band: 26px;

	/* --- the reading measure. In ch, so it tracks the font size instead
	   of rotting when the ramp steps at 1400. 74ch of Open Sans is ~82
	   characters of English prose, which is the top of the comfortable
	   band for a long legal document. --- */
	--pv2p-measure: 74ch;

	/* --- ONE control height for the public surface. The panel's 32px is a
	   TOOLBAR height and reads thin on a marketing page; the auth surface
	   documented 38px for the same reason. 40px is what Ryan approved on
	   the About page's CTA (.ab-btn), stepping to 44 on touch. --- */
	--pv2p-ctl: 40px;

	--pv2p-pad: var(--pv2p-s4);        /* card padding, 26px at >=1400 */
	--pv2p-rail: 1170px;               /* overridden by the width variants */
	/* THE WIDE RAIL — ONE definition, used by three things that must never
	   disagree: the header brand row (PART 1), the .pv2p-w-wide variant
	   below, and the page rail Part 5 gives an unconverted marketing page.
	   Same expression about-v2.css was approved on. */
	--pv2p-rail-wide: min(1560px, calc(100vw - 180px));
	/* THE READING COLUMN. Was baked into .pv2p-w-doc's rail; it is a token
	   now because the rail and the column are two different numbers above
	   1400 — the band goes to --pv2p-rail-wide and the column does not. The
	   value is unchanged: 74ch + the card's own padding = 718px at 1920,
	   which is what /privacy/ and /terms/ measured before this change. */
	--pv2p-doc-col: calc(var(--pv2p-measure) + (var(--pv2p-pad) * 2));
	--pv2p-shadow: 0 10px 26px rgba(0, 0, 0, 0.30);

	/* --- THE WEAVE. One definition, and it is the DEFAULT for flat dark
	   surfaces, not a decoration three components opted into.

	   This is the treatment Ryan named ("that little grid style background
	   that's cool on our main pages", "it has like a cool design"). It was
	   living as three near-copies with three different alphas and two
	   different gaps — .callout at .014/8px, .pv2p-hero at .016/8px,
	   .pv2p-chead.bloom at .018/7px — which is how a house style turns into
	   three house styles. It is one token now and every user references it,
	   so the next surface that needs it cannot invent a fourth value.

	   .014 white at a 8px pitch is deliberately at the edge of visible: it
	   has to kill "flat" at a glance and still never compete with a line of
	   body copy sitting on it. --- */
	--pv2p-weave: repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.014) 0 1px, rgba(255, 255, 255, 0) 1px 8px);
	/* the gold rise that pairs with it on an EMPHASIS surface — a band or a
	   card head, never the page. Kept separate so the weave can be used on
	   its own, which is what a page field wants. */
	--pv2p-bloom: radial-gradient(560px 240px at 100% 0%, rgba(234, 168, 36, 0.13), rgba(234, 168, 36, 0) 72%);
	/* the rung BELOW the page — the CTA band's stage. One step down from
	   --pv2-surface by the ladder's own interval (surface→panel is +7 RGB
	   units; this is −7), so the gold bloom sits on the darkest field on
	   the page. Exists because the CTA callout used to sit ON the page
	   fill: measured across the 323-page crawl (2026-08-05), the band's
	   #1a1a1b equalled its neighbour's on 711 adjacencies over 289 pages —
	   the single largest source of "two same-toned bands read as one
	   slab". A rung of its own can only ever collide with another CTA
	   band, which is a deliberate stack (the ARK/SE platform rows). */
	--pv2p-deep: #131314;
}

/* ------------------------------------------------------------
   THE RAIL — one content width, shared by the band and the body.

   layout.css pins .container to a FIXED 1170px above 1216px, so a
   max-width alone can never widen OR narrow it; `width` has to be reset
   alongside. Same fix about-v2.css carries, generalised into variants so
   every page picks a width instead of inventing one.

     .pv2p-w-doc    a document. Card = the reading measure + its own
                    padding, centred. Legal/policy pages. Deliberately
                    narrow: a 1560px line of body copy is unreadable, and
                    a text page has no figure to spend the width on.
     .pv2p-w-read   an article or a question list. Wider than a document
                    because the lines are short and the rows are chrome.
     .pv2p-w-wide   the About-page rail: min(1560px, 100vw - 180px) above
                    1400. For pages with grids, cards or media that can
                    honestly use the width.
     (no variant)   the theme container, untouched.
   ------------------------------------------------------------ */
body.pubv2 .pv2p > .container,
body.pubv2 .pv2p-band > .container {
	width: 100%;
	max-width: var(--pv2p-rail);
	margin-left: auto;
	margin-right: auto;
}
/* `ch` in --pv2p-rail is resolved AT USE, against the font-size of the
   element using it. The band's own font-size is the theme's, the content
   shell's is the v2 ramp — so a ch-derived rail silently gave the band a
   NARROWER container than the page under it and the title sat 22px inboard
   of its own card (measured on /terms/ at 1440: title 398, card edge 376).
   Pinning the band to the same ramp makes both resolve identically. The
   band's own heading sizes itself, so nothing visible changes here. */
body.pubv2 .pv2p-band { font-size: var(--pv2p-fs-body); }
/* .pv2p-w-doc WAS `--pv2p-doc-col + 30px` — the reading measure used as a
   RAIL, which made the page-title band 748px wide at 1920 and 658px at 768.
   Retired 2026-08-04 for the same reason as .pv2p-w-read below, and kept
   in place for the same reason (five templates carry it).

   THE MEASURE IS NOT RETIRED — it moved to the element that should always
   have owned it. `.pv2p-doc` sets `max-width: min(100%, --pv2p-doc-col)`,
   so the document column is byte-identical at every width (718px at 1920,
   628px at 768) while the band above it now starts on the page rail with
   the logo, the nav and the footer. The design contract's rule holds
   exactly as written: THE CARD IS THE MEASURE. What changed is that the
   BAND is no longer forced to be the card's width too.

   This is the one width decision on this pass that is a judgement rather
   than a repair, so it is worth stating what it trades: the band title on
   /privacy/, /terms/ and the three error pages no longer shares a left
   edge with the column under it (195 vs 601 at 1920). It shares one with
   every other element on the page instead. The alternative — widening the
   column to the rail — is the rejected pattern, twice over: a 1560px line
   of legal copy, and the "74ch cap inside a full-width card / weird
   indentation on the right" note in CLAUDE-DESIGN-SYSTEM.md. */

/* .pv2p-w-read WAS a fixed 980px. RETIRED 2026-08-04 — it now inherits the
   page rail and the class is left in place because four templates carry it
   and markup is not this file's to edit.

   Why it went: 980 is narrower than the theme container from 992px up, so
   on /faq/, /jobs/, /locations/ and /locations/<city>/ the reading rail
   disagreed with the page's own shared CTA band. Measured at 1100: the
   accordion and the location cards started at x=75 while the
   `.callout.styleBackgroundColor` under them started at x=15 and the
   footer at x=15 — three left edges on one page, the middle one being the
   content. Above 1400 the same 980 left it 580px inboard of the header.

   The reading measure it was protecting is still protected, in the place
   the design contract says it belongs: THE CARD IS THE MEASURE
   (`.pv2p-doc`, 718px). A 980 RAIL was never that — it was a rail that
   happened to be narrow, and it made the band disagree with the page. */

/* ---- THE PAGE RAIL ABOVE 1400 — every page's left edge is the SITE's
   left edge. Ryan, 2026-08-04: "a lot of the pages are still not using the
   full width."

   MEASURED AT 1920 BEFORE THIS BLOCK. `#c2brand .container` (the header) is
   180@1560 on every page. The body was:

     home                              1170   (opted in, named no variant)
     faq / jobs / locations / city      980   (.pv2p-w-read)
     privacy / terms / 404 / 502 / 504  748   (.pv2p-w-doc)
     about / affiliates / hytale /
     games browser / 295 game pages    1560   (correct)

   So the pages that got the MOST design attention were the narrowest: a
   page that opted in but named no width fell back to the 1170 theme
   default, and Part 5.0's `:not(:has(.pv2p))` deliberately leaves an
   opted-in page alone. The header logo sat at x=200 with the page title at
   x=485 (faq) or x=600 (privacy) under it — three different left edges on
   one screen, plus the footer's.

   WHAT THIS FIXES, AS A NUMBER: the band container's left edge vs
   `#c2brand .container`'s left edge. 180 vs 470 (faq) and 180 vs 586
   (privacy/terms/404) at 1920. Every page now resolves the SAME
   --pv2p-rail-wide token the header does, so the two can never disagree.

   THE MEASURE IS NOT SPENT ON THE RAIL. This widens the BAND — the
   section, its background, its rhythm — not the line length. Every block
   whose job is reading keeps its own measure INSIDE the wide rail:
   `.pv2p-doc` (the legal column, unchanged at 718px), `.pv2p-prose` on a
   full-rail card, and the accordion's answer body. That is the pattern
   the 295 game pages already ship and Ryan approved — /valheim/'s "What is
   Valheim?" paragraph is a ~940px column centred in a 1560 band.

   Declared on `body.pubv2` (0,1,1) so it is a DEFAULT, not an override: a
   variant declared on the element itself is (0,2,0) and still wins. Only
   `.pv2p-w-wide` is re-stated here — it is the one variant that still
   declares a rail, and it has to, because 1560 is wider than the 1170 this
   default falls back to below 1400. `-read` and `-doc` declare nothing at
   all now (see the notes above them), so they inherit this line.
   ---- */
@media only screen and (min-width: 1400px) {
	body.pubv2 { --pv2p-rail: var(--pv2p-rail-wide); }
	body.pubv2 .pv2p-w-wide { --pv2p-rail: var(--pv2p-rail-wide); }
}

/* ---- THE HOME HERO'S CAPTION RAIL — `.css-hero-inner`.

   mobile-responsive.css:2034 pins it to a flat `max-width: 1170px`. The
   `.css-hero` behind it is full-bleed, so that box is the only thing
   deciding where the slide's logo, tagline and the two CTA buttons land —
   all three are absolutely positioned against it. With the bands on the
   1560 rail the caption sat on a 1170 one and the hero visibly stepped
   INBOARD of everything under it: measured at 1920, tagline left edge 375
   against a band content edge of 195, a 180px step on the first thing
   above the fold.

   The number is the container's CONTENT box, not its border box — a
   Bootstrap `.container` spends 15px of gutter each side, and an
   absolutely-positioned child resolves `left: 0` against the padding box,
   so the caption has to be 30px narrower than the rail to share the same
   left edge. Verified at 1920: 195 = 195.

   Stepped on Bootstrap 3's own container breakpoints below 1400 because
   the mismatch is there too and it is the same defect: 1200-1399 was 15px
   out (375 vs 390), 992-1199 was 80px out (0 vs 80). Below 992 the theme
   deliberately makes the tagline a full-bleed bar (`max-width: none`,
   `left/right: 0`) and that is left alone.
   ---- */
@media only screen and (min-width: 992px) {
	body.pubv2 .css-hero-inner { max-width: 940px; }
}
@media only screen and (min-width: 1200px) {
	body.pubv2 .css-hero-inner { max-width: 1140px; }
}
@media only screen and (min-width: 1400px) {
	body.pubv2 .css-hero-inner { max-width: calc(var(--pv2p-rail-wide) - 30px); }
}

/* ---- THE HOME HERO'S TWO TOUCH FAILURES.

   Measured at 390 with everything else on the page passing: /home/ had 19
   sub-44px targets against a sitewide floor of 3 (the promo bar and the
   staff toggle, both template-owned — see the design contract §9 — plus
   one footer social that passes by hit-extender and only LOOKS small to a
   rect probe). All 16 of the extras are in this hero.

   1. THE SLIDE TAGLINE, x8 — the <a> measured 19x366 inside a 43px bar.
      The bar's 12px of vertical padding sat on the DIV, so it was dead
      space: `.css-hero-tagline` carries z-index 2 and therefore paints
      ABOVE `.css-hero-link` (z-index 1, the full-slide anchor), so a tap
      on the bar's padding hit neither. Moving the same padding onto the
      anchor makes the whole bar the link and takes it to 45px. Total box
      is unchanged (13+19+13 = 45 against 12+19+12 = 43, +2px) and the
      ellipsis rules mobile-responsive.css puts on both elements still
      apply — the anchor is already `display: block` there.

   2. THE ROTATOR DOTS, x8 — 10x10 at a 17px pitch (12x12 / 21px above
      600). Third case of the hit rule: a dense horizontal run keeps its
      mark and takes a hit CELL exactly one pitch wide, so the cells tile
      edge to edge and no dot can fire its neighbour.

      THE INSET IS HALF THE PITCH PLUS THE BORDER, not half the gap. The
      dot is `border: 1px` and `box-sizing: border-box`, so its 10px rect
      is an 8px PADDING box — and an absolutely positioned pseudo resolves
      left/right against the padding box, not the border box. Inset by
      half the gap (3.5px) the cell measured 15px against a 17px pitch and
      elementFromPoint 8px off centre fell through to `.css-hero-link`.
      4.5px a side (5.5 above 600) puts it back on the pitch exactly.

      The cell is anchored to the dot's BOTTOM and grows upward, which is
      the one place this differs from the flexslider dots in PART 5.8.
      Centring a 44px cell on a dot 48px off the hero's bottom edge would
      have run it from 31px to 75px, and the tagline bar occupies 0-45px:
      the bottom 14px of every cell would have stolen taps from the
      tagline link fixed directly above. Anchored at `bottom: -2px` the
      cell runs 46-90 and clears it by 1px. Verified with
      elementFromPoint, not by reading a rect — the rect stays 10x10.
   ---- */
@media (hover: none) and (pointer: coarse), (max-width: 900px) {
	body.pubv2 .css-hero-tagline { padding-top: 0; padding-bottom: 0; }
	body.pubv2 .css-hero-tagline > a { display: block; padding: 13px 22px; }
	body.pubv2 .css-hero-dot { position: relative; }
	body.pubv2 .css-hero-dot:after {
		content: ''; position: absolute;
		left: -5.5px; right: -5.5px; bottom: -2px; height: 44px;
		pointer-events: auto;
	}
}
@media (max-width: 991px) {
	/* the theme drops the tagline's side padding to 15px here and makes it
	   a full-bleed bar; the anchor has to carry the same number or the
	   caption shifts 7px. */
	body.pubv2 .css-hero-tagline > a { padding-left: 15px; padding-right: 15px; }
}
@media (max-width: 600px) {
	/* gap goes 9px -> 7px, dot 12px -> 10px, so the pitch is 17 not 21 */
	body.pubv2 .css-hero-dot:after { left: -4.5px; right: -4.5px; }
}

/* ------------------------------------------------------------
   THE SHELL — everything the theme does to a public page that the v2
   layout assumes is zero. Treat every bare-element rule in layout.css /
   essentials.css as hostile inside .pv2p.
   ------------------------------------------------------------ */
body.pubv2 .pv2p {
	font-family: var(--pv2p-font);
	font-size: var(--pv2p-fs-body);
	line-height: var(--pv2p-lh-body);
	color: var(--pv2-text-soft);
	padding: var(--pv2p-s7) 0 var(--pv2p-s6);
	/* Inherited, so this covers every descendant. A bare URL used as its
	   own link text has no break opportunity in it — measured on
	   /privacy/ at 390, three of them (a PayPal policy URL the worst at
	   345px) ran 9px past the card, and because .pv2p-card clips its
	   overflow the page did NOT scroll: the URL was silently CUT OFF
	   instead. No page-width alarm would ever have caught it. */
	overflow-wrap: break-word;
}
body.pubv2 .pv2p *,
body.pubv2 .pv2p *:before,
body.pubv2 .pv2p *:after { box-sizing: border-box; }

/* layout.css: `section header { margin-bottom: 60px }`. Every card head on
   a public page is a <header> inside a <section>, so each one silently
   carries a 60px void between its title bar and its body. */
body.pubv2 .pv2p header,
body.pubv2 .pv2p-band header { margin: 0; }

/* layout.css: `input, select, textarea { margin-bottom: 10px }` inflates
   every wrapper past the control it holds — the bug class that put the
   panel's search glyphs 5px low on three separate pages. Zero it once. */
body.pubv2 .pv2p input,
body.pubv2 .pv2p select,
body.pubv2 .pv2p textarea { margin-bottom: 0; }

/* links. Declared HERE, before every component, so a component can
   out-specify it by tag (a.pv2p-btn) rather than by !important. */
body.pubv2 .pv2p a {
	color: var(--pv2-accent);
	text-decoration: none;
	transition: color .12s;
}
body.pubv2 .pv2p a:hover,
body.pubv2 .pv2p a:focus { color: var(--pv2-gold); text-decoration: none; }
body.pubv2 .pv2p a:focus-visible {
	outline: 2px solid rgba(213, 168, 74, .45);
	outline-offset: 2px; border-radius: 2px;
}

/* ------------------------------------------------------------
   THE BAND — section.page-title. Site chrome stays site chrome: it keeps
   its own background and position, only its type comes onto the v2 ramp.
   The theme spends a `padding-bottom:15px` on a hairline underline that
   is invisible on a dark background, so it is cancelled and the spacing
   is set explicitly (about-v2.css found this first).
   ------------------------------------------------------------ */
body.pubv2 .pv2p-band { padding: 22px 0 20px; }
body.pubv2 .pv2p-band h1,
body.pubv2 .pv2p-band h2 {
	font-family: var(--pv2p-font);
	font-size: var(--pv2p-fs-band); font-weight: 400; color: #fff;
	letter-spacing: .01em; line-height: 1.25;
	margin: 0; padding-bottom: 0; border-bottom: 0; display: block;
}
body.pubv2 .pv2p-band .pv2p-band-sub {
	margin: 9px 0 0; padding: 0; border: 0;
	font-size: 13.5px; line-height: 1.5; color: var(--pv2-text-muted);
}

/* ------------------------------------------------------------
   PROSE — the bare-element ramp. Applied inside .pv2p-prose so a page can
   hand a whole region of untouched editorial markup to the system without
   adding a class to 400 paragraphs.

   Headings are sized BY LEVEL here because that is all a prose region
   knows. When a page needs a different visual weight for a level it must
   NOT change, it wraps that block in a component (.pv2p-chead, .pv2p-sec)
   and the component wins.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-prose { font-size: var(--pv2p-fs-body); line-height: var(--pv2p-lh-body); color: var(--pv2-text-soft); }
body.pubv2 .pv2p-prose > *:first-child { margin-top: 0; }
body.pubv2 .pv2p-prose > *:last-child { margin-bottom: 0; }

/* THE NEUTRALISER — declare, never inherit.
   Two bare-element rules in the shared theme beat inheritance outright,
   because an inherited value loses to ANY declared rule no matter how
   weak the selector:
     essentials.css:853  `p { line-height: 22px }`   a hardcoded PIXEL
        leading. At the 15.5px ramp that is 1.42, so paragraphs rendered
        visibly tighter than the list items beside them (measured: p 22px
        vs li 25.73px in the same card).
     layout-dark.css:~35 `p, ul, li, ol li, cite, blockquote cite, h1-h6
        { color: #fff }`  which painted an entire legal document pure
        white, flattening it against its own white headings.
   Both are invisible in a diff and neither throws anything. Any new
   element type added to the prose ramp must be added to this list too. */
body.pubv2 .pv2p-prose p,
body.pubv2 .pv2p-prose ul,
body.pubv2 .pv2p-prose ol,
body.pubv2 .pv2p-prose li,
body.pubv2 .pv2p-prose dl,
body.pubv2 .pv2p-prose dd,
body.pubv2 .pv2p-prose cite,
body.pubv2 .pv2p-prose blockquote cite {
	color: var(--pv2-text-soft);
	line-height: var(--pv2p-lh-body);
}

body.pubv2 .pv2p-prose h1,
body.pubv2 .pv2p-prose h2,
body.pubv2 .pv2p-prose h3,
body.pubv2 .pv2p-prose h4,
body.pubv2 .pv2p-prose h5,
body.pubv2 .pv2p-prose h6 {
	font-family: var(--pv2p-font); color: #fff;
	margin: var(--pv2p-s5) 0 var(--pv2p-s2); padding: 0;
	border: 0; text-transform: none; letter-spacing: 0;
}
body.pubv2 .pv2p-prose h1 { font-size: 24px; font-weight: 400; line-height: 1.25; }
body.pubv2 .pv2p-prose h2 { font-size: 19px; font-weight: 600; line-height: 1.3; }
body.pubv2 .pv2p-prose h3 { font-size: 16px; font-weight: 600; line-height: 1.35; }
body.pubv2 .pv2p-prose h4 { font-size: 14.5px; font-weight: 600; line-height: 1.4; }
body.pubv2 .pv2p-prose h5,
body.pubv2 .pv2p-prose h6 {
	font-size: 11px; font-weight: 600; letter-spacing: .09em;
	text-transform: uppercase; color: var(--pv2-text-muted);
	margin-bottom: var(--pv2p-s1);
}
body.pubv2 .pv2p-prose p { margin: 0 0 var(--pv2p-s3); }
body.pubv2 .pv2p-prose strong,
body.pubv2 .pv2p-prose b { color: #fff; font-weight: 600; }
/* `i` is BOTH the italic element and Font Awesome's carrier tag on this site.
   `body.pubv2 .pv2p-prose i` is (0,2,2) and fa-subset's `.fa { font-style:
   normal }` is (0,1,0), so the prose ramp skewed every icon inside it — which
   is why two pages could not put .pv2p-prose on any block that carries an
   icon, and reported it rather than half-applying the ramp. Exclude anything
   wearing an fa class: FA6 always needs a family class AND an icon class, and
   both match [class*="fa-"]. */
body.pubv2 .pv2p-prose em,
body.pubv2 .pv2p-prose i:not([class*="fa-"]) { font-style: italic; }
body.pubv2 .pv2p-prose i[class*="fa-"] { font-style: normal; }
body.pubv2 .pv2p-prose small { font-size: 11.5px; color: var(--pv2-text-dim); }
body.pubv2 .pv2p-prose code,
body.pubv2 .pv2p-mono {
	font-family: var(--pv2-mono); font-size: 12.5px;
	background: var(--pv2-field); border: 1px solid var(--pv2-line);
	border-radius: 3px; padding: 1px 5px; color: #e6e6e6;
}
/* the theme's separator. On dark it was an invisible rgba hairline doing
   nothing but spending vertical space — make it a real, quiet divider. */
body.pubv2 .pv2p-prose hr,
body.pubv2 .pv2p hr {
	height: 1px; border: 0; background: var(--pv2-line);
	margin: var(--pv2p-s5) 0; opacity: 1;
}
body.pubv2 .pv2p-prose hr.invisible,
body.pubv2 .pv2p hr.invisible { background: transparent; }

/* lists. `list-style-position: outside` keeps the marker in the gutter so
   a wrapped line stays flush with the first, and the marker is dimmed so
   it never outweighs the sentence it numbers. */
body.pubv2 .pv2p-prose ul,
body.pubv2 .pv2p-prose ol {
	margin: 0 0 var(--pv2p-s3); padding-left: 22px;
	list-style-position: outside;
}
body.pubv2 .pv2p-prose li { margin: 0 0 var(--pv2p-s1); padding-left: 2px; }
body.pubv2 .pv2p-prose li:last-child { margin-bottom: 0; }
body.pubv2 .pv2p-prose li::marker { color: var(--pv2-text-dim); }
body.pubv2 .pv2p-prose li > p { margin-bottom: var(--pv2p-s1); }
body.pubv2 .pv2p-prose ul ul,
body.pubv2 .pv2p-prose ol ol,
body.pubv2 .pv2p-prose ul ol,
body.pubv2 .pv2p-prose ol ul { margin-top: var(--pv2p-s1); margin-bottom: var(--pv2p-s2); }
body.pubv2 .pv2p-prose blockquote {
	margin: 0 0 var(--pv2p-s3); padding: 11px 14px; border: 0;
	border-left: 3px solid var(--pv2-accent);
	background: rgba(255, 255, 255, 0.026);
	border-radius: 0 var(--pv2-radius) var(--pv2-radius) 0;
	font-size: 13.5px; line-height: 1.55; color: #dcdcdc;
}
body.pubv2 .pv2p-prose img { max-width: 100%; height: auto; }

/* ------------------------------------------------------------
   SPACING BLOCKS
   ------------------------------------------------------------ */
body.pubv2 .pv2p-sec { margin: 0 0 var(--pv2p-s6); }
body.pubv2 .pv2p-sec:last-child { margin-bottom: 0; }
body.pubv2 .pv2p-stack { display: flex; flex-direction: column; gap: var(--pv2p-s5); }

/* ------------------------------------------------------------
   CARD — the one surface. Mirrors .pv2-card / .ab-card exactly:
   --pv2-panel body, --pv2-border hairline, 6px radius, --pv2-surface head.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-card {
	background: var(--pv2-panel);
	border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius-lg);
	box-shadow: var(--pv2p-shadow);
	overflow: hidden;
}
/* The stacked-card gap. It is a FLOW rule and it must not fire inside a
   layout container that already owns its own spacing — in a grid the
   adjacent-sibling margin lands on grid ITEMS, so cards 2 and 3 of a
   three-across row sit 26px lower than card 1 (measured on /locations/ at
   1440: y = 751 / 777 / 777, and the page had to wrap every card in a bare
   <div> to stop them being siblings). In a .pv2p-stack it doubles the 26px
   gap to 52. Reset it in all four containers rather than making every page
   work around it. */
body.pubv2 .pv2p-card + .pv2p-card { margin-top: var(--pv2p-s5); }
body.pubv2 .pv2p-grid > .pv2p-card + .pv2p-card,
body.pubv2 .pv2p-stack > .pv2p-card + .pv2p-card,
body.pubv2 .pv2p-split > .pv2p-card + .pv2p-card,
body.pubv2 .pv2p-media > .pv2p-card + .pv2p-card { margin-top: 0; }
body.pubv2 .pv2p-card.flat { box-shadow: none; background: transparent; }
body.pubv2 .pv2p-card.accent { border-color: var(--pv2-border-strong); }

/* the card head bar. Styles WHATEVER heading tag it is handed — that is
   how a frozen heading level still reads as a section head. */
body.pubv2 .pv2p-chead {
	position: relative; overflow: hidden;
	display: flex; align-items: center; gap: var(--pv2p-s2);
	min-height: 44px; padding: 11px var(--pv2p-pad); margin: 0;
	background: var(--pv2-surface);
	border-bottom: 1px solid var(--pv2-line);
}
body.pubv2 .pv2p-chead > h1,
body.pubv2 .pv2p-chead > h2,
body.pubv2 .pv2p-chead > h3,
body.pubv2 .pv2p-chead > h4,
body.pubv2 .pv2p-chead > h5,
body.pubv2 .pv2p-chead > h6 {
	position: relative; margin: 0; padding: 0; border: 0;
	font-family: var(--pv2p-font);
	font-size: 15px; font-weight: 600; line-height: 1.35; color: #fff;
	letter-spacing: 0; text-transform: none; min-width: 0;
}
body.pubv2 .pv2p-chead-actions { margin-left: auto; display: flex; align-items: center; gap: var(--pv2p-s2); flex-shrink: 0; }
/* a head with no artwork gets a deliberate treatment, not a bare strip:
   a gold bloom from the top right over a fine diagonal weave. Same idea
   as the About cards without band art and the dashboard game cards. */
body.pubv2 .pv2p-chead.bloom:before {
	content: ''; position: absolute; inset: 0; pointer-events: none;
	background:
		radial-gradient(150px 90px at 100% -30%, rgba(234, 168, 36, 0.16), rgba(234, 168, 36, 0) 70%),
		var(--pv2p-weave);
}
body.pubv2 .pv2p-cbody { padding: var(--pv2p-pad); }
body.pubv2 .pv2p-cbody > *:first-child { margin-top: 0; }
body.pubv2 .pv2p-cbody > *:last-child { margin-bottom: 0; }

/* ------------------------------------------------------------
   DOCUMENT — a long single-column read (policy, terms, an article).
   The CARD is the measure: the reading column is the card body, so
   nothing is a narrow block floating inside a wide box. That is the
   "weird indentation on the right" rejection in CLAUDE-DESIGN-SYSTEM.md,
   avoided by construction rather than by a max-width on the paragraphs.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-doc { margin: 0 auto; max-width: min(100%, var(--pv2p-doc-col)); }
body.pubv2 .pv2p-doc .pv2p-cbody { padding: var(--pv2p-s5) var(--pv2p-pad); }
/* a document's own dateline / revision note, above the first section.
   Carries NO rule of its own — a legal page already ships an <hr> after
   the dateline and two dividers in 30px reads as a mistake. */
body.pubv2 .pv2p-docmeta {
	margin: 0; padding: 0; border: 0;
	font-size: 12px; line-height: 1.5; color: var(--pv2-text-dim);
}
body.pubv2 .pv2p-docmeta em,
body.pubv2 .pv2p-docmeta i { font-style: normal; }

/* ---------- LEGAL — a numbered document (.pv2p-legal on the prose) ----------
   The clause numbering is CONTENT: it is what a support reply or a
   chargeback response cites. It comes from `<ol type="I">` and its nested
   `<ol type="a">`, and the markers are drawn by the browser, so the only
   safe way to make the outline read is to style ::marker and hang it in a
   gutter — never to restructure the list.

   The top-level headings are `<h4><li>…</li></h4>` inside the <ol>. That
   is invalid nesting the parser keeps as-is, so the <li> is still a list
   item of the <ol> and still increments its counter. Style it in place;
   do NOT "fix" the nesting — moving the <li> renumbers the contract. */
body.pubv2 .pv2p-legal > ol { padding-left: 46px; margin-bottom: var(--pv2p-s4); }
body.pubv2 .pv2p-legal > ol > h4 {
	margin: var(--pv2p-s6) 0 var(--pv2p-s3); padding: 0;
	font-size: 17px; font-weight: 600; line-height: 1.3; color: #fff;
}
body.pubv2 .pv2p-legal > ol > h4:first-child { margin-top: 0; }
body.pubv2 .pv2p-legal > ol > h4 > li { margin: 0; padding: 0; }
body.pubv2 .pv2p-legal > ol > h4 > li::marker {
	color: var(--pv2-gold); font-weight: 700; font-size: 15px;
}
/* clause bodies: a hair smaller and quieter than the section heads, with
   the marker dim so the sentence outweighs its own number. */
body.pubv2 .pv2p-legal ol ol { padding-left: 26px; }
body.pubv2 .pv2p-legal ol ol > li { margin-bottom: var(--pv2p-s2); }
body.pubv2 .pv2p-legal ol ol > li::marker { color: var(--pv2-text-muted); font-weight: 600; }
body.pubv2 .pv2p-legal ol ol ol > li::marker { color: var(--pv2-text-dim); font-weight: 400; }
/* the separators between clauses sit INSIDE the <ol>, so they are list
   children with no marker of their own — keep them quiet and tight. */
body.pubv2 .pv2p-legal > ol > hr { margin: var(--pv2p-s5) 0 0; }
body.pubv2 .pv2p-legal > ol > p { margin-bottom: var(--pv2p-s3); }
@media only screen and (max-width: 560px) {
	body.pubv2 .pv2p-legal > ol { padding-left: 30px; }
	body.pubv2 .pv2p-legal > ol > h4 { font-size: 16px; }
	body.pubv2 .pv2p-legal ol ol { padding-left: 21px; }
}

/* ---------- CONTENTS — the block a long document opens with (.pv2p-toc) ----
   /terms/ is 13 sections of numbered clauses with nothing to navigate by;
   the reader arriving from a support reply is looking for ONE clause. The
   markup is Smarty-gated ({if $ssPubV2}) in the template — same as the id
   anchors it points at — so the classic render stays byte-identical.
   Built for /terms/ AND /privacy/: both document pages use this one
   component, never a per-page lookalike. */
body.pubv2 nav.pv2p-toc {
	margin: var(--pv2p-s4) 0 var(--pv2p-s2);
	padding: var(--pv2p-s3) var(--pv2p-s4);
	background: var(--pv2-field);
	border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius-lg);
}
body.pubv2 .pv2p-toc-label {
	display: block; margin: 0 0 var(--pv2p-s1);
	font-size: 10.5px; font-weight: 700; letter-spacing: .08em;
	text-transform: uppercase; color: var(--pv2-text-muted);
}
/* the ol tag is load-bearing: .pv2p-prose ol is (0,2,2) and a bare
   .pv2p-toc-list would be (0,2,1) and lose its list-style/padding reset
   to the prose ramp. Declared after the prose + legal blocks so the
   (0,2,2) ties resolve here. */
body.pubv2 ol.pv2p-toc-list { list-style: none; margin: 0; padding: 0; }
@media only screen and (min-width: 600px) {
	/* 13 single-line rows in one column is a screen of chrome before the
	   document starts; two balanced columns halve it. break-inside on the
	   rows keeps a link from splitting across the gap. */
	body.pubv2 ol.pv2p-toc-list { columns: 2; column-gap: var(--pv2p-s5); }
}
body.pubv2 ol.pv2p-toc-list > li { margin: 0; padding: 0; break-inside: avoid; }
body.pubv2 ol.pv2p-toc-list > li > a {
	display: flex; align-items: baseline; gap: 9px;
	padding: 3px 2px;
	color: var(--pv2-text-soft);
}
body.pubv2 ol.pv2p-toc-list > li > a:hover,
body.pubv2 ol.pv2p-toc-list > li > a:focus { color: var(--pv2-gold); }
/* the numeral mirrors the document's own gold clause markers
   (.pv2p-legal > ol > h4 > li::marker), right-aligned in a fixed gutter
   so the titles share one left edge. */
body.pubv2 .pv2p-toc-n {
	flex: 0 0 26px; text-align: right;
	font-size: 12px; font-weight: 600; color: var(--pv2-gold);
}
/* the rows own their line -> grow the row (hit-rule case 1). Padding, not
   min-height: a min-height box under align-items:baseline parks the text
   at the top of the 44px cell instead of centring it. 22.7px line box
   + 2x11px = 44.7px. */
@media (hover: none) and (pointer: coarse), (max-width: 900px) {
	body.pubv2 ol.pv2p-toc-list > li > a { padding: 11px 2px; }
}
/* anchor landing: a heading that jumps flush against the viewport edge
   reads as cut off; 14px of scroll margin keeps a breath above it. On
   .pv2p-doc so /privacy/ inherits it for its own anchors. */
body.pubv2 .pv2p-doc [id] { scroll-margin-top: 14px; }

/* ------------------------------------------------------------
   GRID + SPLIT
   ------------------------------------------------------------ */
body.pubv2 .pv2p-grid {
	display: grid; gap: var(--pv2p-s5);
	grid-template-columns: minmax(0, 1fr);
	align-items: start;
}
/* FIXED track counts, never auto-fit: `repeat(auto-fit, minmax(Npx,1fr))`
   silently wraps 5+1 when the tiles do not divide into the rail, and a
   ragged wrap reads as sloppy (KPI strip rejection, CLAUDE-DESIGN-SYSTEM). */
@media only screen and (min-width: 700px) {
	body.pubv2 .pv2p-grid.cols-2,
	body.pubv2 .pv2p-grid.cols-3,
	body.pubv2 .pv2p-grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media only screen and (min-width: 1000px) {
	body.pubv2 .pv2p-grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
	body.pubv2 .pv2p-grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* THE ORPHAN. Fixed track counts stop the *silent* 5+1 wrap, but they cannot
   stop 3 cards landing in 2 tracks — measured on /affiliates/ and
   /hytale-creators/ at 768: a cols-3 grid rendering 2 + 1 with a half-empty
   last row, which is the same ragged read this contract rejects. When the
   last item would sit alone on its row it spans the full width instead, so
   the row reads as a deliberate wide card. Positional selectors are fine
   HERE — this is our own component with a known DOM, not a game-page
   section with 64 different orderings. */
@media only screen and (min-width: 700px) and (max-width: 999px) {
	body.pubv2 .pv2p-grid.cols-2 > *:last-child:nth-child(odd),
	body.pubv2 .pv2p-grid.cols-3 > *:last-child:nth-child(odd),
	body.pubv2 .pv2p-grid.cols-4 > *:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
@media only screen and (min-width: 1000px) {
	body.pubv2 .pv2p-grid.cols-2 > *:last-child:nth-child(odd) { grid-column: 1 / -1; }
	body.pubv2 .pv2p-grid.cols-3 > *:last-child:nth-child(3n + 1) { grid-column: 1 / -1; }
	body.pubv2 .pv2p-grid.cols-4 > *:last-child:nth-child(4n + 1) { grid-column: 1 / -1; }
}
/* `.even` means equal-height cards. `grid-auto-rows: 1fr` sizes the TRACK;
   the item still has to fill it, and .pv2p-grid's own `align-items: start`
   was stopping that — so `.even` did nothing visible and pages measured
   ragged card bottoms. Both halves are needed. */
body.pubv2 .pv2p-grid.even { grid-auto-rows: 1fr; align-items: stretch; }
body.pubv2 .pv2p-grid.even > *,
body.pubv2 .pv2p-grid.even > * > .pv2p-card { height: 100%; }
/* Bootstrap 3 .row uses ::before/::after as a clearfix. Inside a grid
   container those pseudos become GRID ITEMS and take real cells, which
   pushes every card one slot right and leaves a ragged tail. .pv2p-split
   has always killed them; .pv2p-grid and .pv2p-media did not, so neither
   could be laid over an existing `.row` — the gap the home-page agent hit.
   Harmless when the element is not a .row (there is no pseudo to remove). */
body.pubv2 .pv2p-grid:before,
body.pubv2 .pv2p-grid:after,
body.pubv2 .pv2p-media:before,
body.pubv2 .pv2p-media:after { content: none; }
body.pubv2 .pv2p-grid,
body.pubv2 .pv2p-media { margin-left: 0; margin-right: 0; }

/* .pv2p-cell — what .pv2p-main / .pv2p-aside are to .pv2p-split, for the
   other two grids. A page ADDS it beside the .col-md-* that is already on
   the element (RENAME NOTHING): the class stays, its float, width and
   gutter padding stop applying, and the grid owns the track. */
body.pubv2 .pv2p-cell {
	min-width: 0;
	float: none; width: auto;
	padding-left: 0; padding-right: 0;
	margin-left: 0; margin-right: 0;
}
body.pubv2 .pv2p-cell:before,
body.pubv2 .pv2p-cell:after { content: none; }

/* .pv2p-nest — a .container nested inside another .container pays the
   15px gutter twice and lands its content 30px inboard of everything
   above it. On the game pages `.container > .container.margin-top60` does
   exactly that on 293 templates. Add this beside the inner .container. */
body.pubv2 .pv2p-nest {
	width: auto; max-width: none;
	padding-left: 0; padding-right: 0;
	margin-left: 0; margin-right: 0;
}

/* main + aside. One column below 900 with the aside AFTER the main —
   a sidebar is never the first thing a phone shows.

   THE BOOTSTRAP CLASSES STAY ON THE MARKUP. `.row` / `.col-md-*` are
   matched by JS elsewhere on this site (the order page rebuilds its form
   by walking exactly those hooks), so a page ADDS .pv2p-split /
   .pv2p-main / .pv2p-aside beside them and never renames them. Which
   means this component has to neutralise the float grid it is layered
   over — and it does that HERE, once, through its own class, never with
   a bare `.row` or `.col-md-6` rule. */
body.pubv2 .pv2p-split {
	display: grid; gap: var(--pv2p-s5);
	grid-template-columns: minmax(0, 1fr); align-items: start;
	margin-left: 0; margin-right: 0;   /* the .row negative gutter */
}
/* Bootstrap 3 .row uses ::before/::after as a clearfix; inside a grid
   container those pseudos become GRID ITEMS and occupy real cells, which
   lands the cards in the wrong columns (the exact trap the Death Star KPI
   row hit). Kill them in grid mode only. */
body.pubv2 .pv2p-split:before,
body.pubv2 .pv2p-split:after { content: none; }
body.pubv2 .pv2p-main,
body.pubv2 .pv2p-aside {
	min-width: 0;
	float: none; width: auto;
	padding-left: 0; padding-right: 0;
}
/* a bare .row wrapping content that is NOT a bootstrap column: the -15px
   gutter would push the block past the container's own padding and, on a
   phone, straight to the viewport edge. Scoped through .pv2p-plain, which
   the page adds; the .row class itself is left on the element. */
body.pubv2 .pv2p .pv2p-plain { margin-left: 0; margin-right: 0; }
body.pubv2 .pv2p .pv2p-plain:before,
body.pubv2 .pv2p .pv2p-plain:after { content: none; }
@media only screen and (min-width: 900px) {
	body.pubv2 .pv2p-split { grid-template-columns: minmax(0, 1fr) clamp(260px, 30%, 340px); gap: var(--pv2p-s5) var(--pv2p-s6); }
	body.pubv2 .pv2p-split.rev { grid-template-columns: clamp(260px, 30%, 340px) minmax(0, 1fr); }
	body.pubv2 .pv2p-split.rev > .pv2p-main { order: 2; }
	body.pubv2 .pv2p-split.rev > .pv2p-aside { order: 1; }
}

/* ------------------------------------------------------------
   WELL / NOTE / QUOTE
   `.alert` is a Bootstrap class and must never be used as a modifier
   (CLAUDE-DESIGN-SYSTEM) — the public note component is .pv2p-note.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-well {
	background: var(--pv2-field); border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius); padding: var(--pv2p-s3) var(--pv2p-s4);
}
body.pubv2 .pv2p-note {
	position: relative; padding: 12px 14px; margin: 0 0 var(--pv2p-s3);
	background: var(--pv2-field); border: 1px solid var(--pv2-border);
	border-left: 3px solid var(--pv2-border-strong);
	border-radius: var(--pv2-radius);
	font-size: 13.5px; line-height: 1.55; color: var(--pv2-text-soft);
}
body.pubv2 .pv2p-note > *:first-child { margin-top: 0; }
body.pubv2 .pv2p-note > *:last-child { margin-bottom: 0; }
body.pubv2 .pv2p-note.ok { border-left-color: var(--pv2-ok); background: var(--pv2-ok-bg); }
body.pubv2 .pv2p-note.warn { border-left-color: var(--pv2-warn); background: var(--pv2-warn-bg); }
body.pubv2 .pv2p-note.danger { border-left-color: var(--pv2-danger); background: var(--pv2-danger-bg); }
body.pubv2 .pv2p-note.info { border-left-color: var(--pv2-info); background: var(--pv2-info-bg); }
body.pubv2 .pv2p-quote {
	margin: 0 0 var(--pv2p-s3); padding: 11px 14px; border: 0;
	border-left: 3px solid var(--pv2-accent);
	background: rgba(255, 255, 255, 0.026);
	border-radius: 0 var(--pv2-radius) var(--pv2-radius) 0;
	font-size: 13.5px; line-height: 1.55; color: #dcdcdc;
}
body.pubv2 .pv2p-quote p { margin: 0; font-size: inherit; line-height: inherit; color: inherit; }
body.pubv2 .pv2p-quote cite { display: block; margin: 7px 0 0; font-size: 11.5px; font-style: normal; color: var(--pv2-text-dim); }

/* ------------------------------------------------------------
   TYPE HELPERS
   ------------------------------------------------------------ */
body.pubv2 .pv2p-lede {
	margin: 0 0 var(--pv2p-s4); font-size: 15.5px; line-height: 1.6;
	font-weight: 400; color: #e8e8e8;
}
body.pubv2 .pv2p-label {
	display: block; margin: 0 0 var(--pv2p-s2); padding: 0;
	font-family: var(--pv2p-font);
	font-size: 10.5px; font-weight: 600; letter-spacing: .09em;
	text-transform: uppercase; color: var(--pv2-text-dim);
}
body.pubv2 .pv2p-meta { font-size: 11.5px; line-height: 1.45; color: var(--pv2-text-dim); }

/* .pv2p-shead — THE MARKETING SECTION HEAD. The third size the system was
   missing: .pv2p-chead is a 15px chrome bar for a card, .pv2p-prose h2 is a
   19px heading inside running copy, and neither is the 22-26px head that
   opens a marketing band. Styles WHATEVER heading tag it is handed, for the
   same reason .pv2p-chead does — the outline is frozen SEO content.
   Put it on the wrapper, or directly on the heading. */
body.pubv2 .pv2p-shead {
	margin: 0 0 var(--pv2p-s4); padding: 0; border: 0;
}
body.pubv2 .pv2p-shead > h1,
body.pubv2 .pv2p-shead > h2,
body.pubv2 .pv2p-shead > h3,
body.pubv2 .pv2p-shead > h4,
body.pubv2 .pv2p-shead > h5,
body.pubv2 .pv2p-shead > h6,
body.pubv2 h1.pv2p-shead,
body.pubv2 h2.pv2p-shead,
body.pubv2 h3.pv2p-shead,
body.pubv2 h4.pv2p-shead,
body.pubv2 h5.pv2p-shead,
body.pubv2 h6.pv2p-shead {
	font-family: var(--pv2p-font);
	font-size: 20px; font-weight: 400; line-height: 1.35; color: #fff;
	margin: 0 0 var(--pv2p-s2); padding: 0; border: 0;
	text-transform: none; letter-spacing: .005em;
}
body.pubv2 .pv2p-shead-sub,
body.pubv2 .pv2p-shead > p {
	margin: 0; padding: 0; border: 0;
	font-size: 14.5px; line-height: 1.6; color: var(--pv2-text-muted);
	max-width: 78ch;
}
@media only screen and (min-width: 1400px) {
	body.pubv2 .pv2p-shead > h1,
	body.pubv2 .pv2p-shead > h2,
	body.pubv2 .pv2p-shead > h3,
	body.pubv2 .pv2p-shead > h4,
	body.pubv2 h1.pv2p-shead,
	body.pubv2 h2.pv2p-shead,
	body.pubv2 h3.pv2p-shead,
	body.pubv2 h4.pv2p-shead { font-size: 22px; }
}
@media only screen and (max-width: 560px) {
	body.pubv2 .pv2p-shead > h1,
	body.pubv2 .pv2p-shead > h2,
	body.pubv2 .pv2p-shead > h3,
	body.pubv2 .pv2p-shead > h4,
	body.pubv2 h1.pv2p-shead,
	body.pubv2 h2.pv2p-shead,
	body.pubv2 h3.pv2p-shead,
	body.pubv2 h4.pv2p-shead { font-size: 18px; }
}

/* ------------------------------------------------------------
   BUTTONS — mirror .pv2-btn / .ab-btn. The gold is spent on the ONE
   primary action per view; everything else is dark chrome. Three solid
   gold buttons in a row is the "way too bright" rejection.
   ------------------------------------------------------------ */
/* SCOPE: the button does NOT require a .pv2p shell. It was originally
   written `body.pubv2 .pv2p .pv2p-btn` for every variant, which meant a CTA
   in an absolutely-positioned hero overlay — where a .pv2p shell cannot go —
   got the base rule and silently lost its hover, its variants and its 44px
   touch floor. Reported by the home-page agent; the page correctly left
   bootstrap buttons in place rather than half-apply a component.

   The `a.` / `button.` companions are not decoration. `body.pubv2 .pv2p a`
   is (0,2,2) and `body.pubv2 .pv2p a:hover` is (0,3,2); a bare
   `.pv2p-btn` (0,2,1) and `.pv2p-btn:hover` (0,3,1) both LOSE to them
   inside a shell and the button repaints gold-on-gold. The tagged forms tie
   and win on source order, which is why they are declared after the link
   rule. The .primary/.ghost/.sm/.lg variants are (0,3,1)+ and clear the
   link rule on their own. */
body.pubv2 .pv2p-btn,
body.pubv2 a.pv2p-btn,
body.pubv2 button.pv2p-btn {
	display: inline-flex; align-items: center; justify-content: center; gap: 8px;
	height: var(--pv2p-ctl); padding: 0 20px;
	background: transparent; border: 1px solid rgba(255, 255, 255, 0.5);
	border-radius: var(--pv2-radius); color: #fff;
	font-family: var(--pv2p-font); font-size: 14px; font-weight: 400;
	line-height: 1; letter-spacing: .01em; white-space: nowrap;
	text-decoration: none; text-shadow: none; box-shadow: none; cursor: pointer;
	transition: background-color .12s, border-color .12s, color .12s;
}
body.pubv2 .pv2p-btn:hover,
body.pubv2 .pv2p-btn:focus,
body.pubv2 a.pv2p-btn:hover,
body.pubv2 a.pv2p-btn:focus,
body.pubv2 button.pv2p-btn:hover,
body.pubv2 button.pv2p-btn:focus {
	background-color: var(--pv2-hover); border-color: rgba(255, 255, 255, 0.5);
	color: #fff; text-decoration: none;
}
/* The variants carry the tag too, and this one is measured, not theoretical:
   Part 3's `.callout .btn` is (0,3,1) and Part 3 is LATER in this file, so a
   bare `.pv2p-btn.lg` (0,3,1) tied with it and lost — the "Learn More About
   Creator Program" button inside the /affiliates/ callout dropped from 46px
   to 40px the moment the variants stopped requiring a .pv2p shell. The
   tagged forms are (0,3,2) and clear the shared-vocabulary layer outright. */
body.pubv2 .pv2p-btn.primary,
body.pubv2 a.pv2p-btn.primary,
body.pubv2 button.pv2p-btn.primary {
	background: var(--pv2-gold); border-color: var(--pv2-gold);
	color: #fff; font-weight: 600;
}
body.pubv2 .pv2p-btn.primary:hover,
body.pubv2 .pv2p-btn.primary:focus,
body.pubv2 a.pv2p-btn.primary:hover,
body.pubv2 a.pv2p-btn.primary:focus,
body.pubv2 button.pv2p-btn.primary:hover,
body.pubv2 button.pv2p-btn.primary:focus {
	background: var(--pv2-gold-deep); border-color: var(--pv2-gold-deep); color: #fff;
}
body.pubv2 .pv2p-btn.ghost,
body.pubv2 a.pv2p-btn.ghost,
body.pubv2 button.pv2p-btn.ghost { background: transparent; border-color: var(--pv2-border-strong); color: var(--pv2-text-soft); }
body.pubv2 .pv2p-btn.ghost:hover,
body.pubv2 a.pv2p-btn.ghost:hover,
body.pubv2 button.pv2p-btn.ghost:hover { background: var(--pv2-hover); color: #fff; }
body.pubv2 .pv2p-btn.sm,
body.pubv2 a.pv2p-btn.sm,
body.pubv2 button.pv2p-btn.sm { height: 32px; padding: 0 14px; font-size: 13px; }
body.pubv2 .pv2p-btn.lg,
body.pubv2 a.pv2p-btn.lg,
body.pubv2 button.pv2p-btn.lg { height: 46px; padding: 0 26px; font-size: 15px; }
body.pubv2 .pv2p-btn.block,
body.pubv2 a.pv2p-btn.block,
body.pubv2 button.pv2p-btn.block { width: 100%; }
body.pubv2 .pv2p-btn[disabled],
body.pubv2 .pv2p-btn.disabled { opacity: .45; cursor: not-allowed; }
body.pubv2 .pv2p-btnrow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pv2p-s2); }

/* ------------------------------------------------------------
   PILLS + CHIPS
   ------------------------------------------------------------ */
body.pubv2 .pv2p-pill {
	display: inline-flex; align-items: center; gap: 6px;
	height: 22px; padding: 0 9px; border-radius: 11px;
	font-family: var(--pv2p-font); font-size: 11px; font-weight: 600;
	letter-spacing: .03em; white-space: nowrap;
	background: var(--pv2-field); color: var(--pv2-text-soft);
	border: 1px solid var(--pv2-border);
}
body.pubv2 .pv2p-pill.ok { background: var(--pv2-ok-bg); color: var(--pv2-ok); border-color: rgba(100, 217, 119, 0.35); }
body.pubv2 .pv2p-pill.warn { background: var(--pv2-warn-bg); color: var(--pv2-warn); border-color: rgba(240, 173, 78, 0.35); }
body.pubv2 .pv2p-pill.danger { background: var(--pv2-danger-bg); color: var(--pv2-danger); border-color: rgba(242, 87, 87, 0.35); }
body.pubv2 .pv2p-pill.info { background: var(--pv2-info-bg); color: var(--pv2-info); border-color: rgba(91, 192, 222, 0.35); }
body.pubv2 .pv2p-pill.gold { background: var(--pv2-surface); color: var(--pv2-gold); border-color: var(--pv2-border-strong); }

body.pubv2 .pv2p-chiplist { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0; padding: 0; list-style: none; }
body.pubv2 .pv2p-chiplist > li { margin: 0; }
body.pubv2 .pv2p-chip,
body.pubv2 .pv2p a.pv2p-chip {
	display: inline-flex; align-items: center; gap: 8px;
	height: 28px; padding: 0 11px; border-radius: 999px;
	background: var(--pv2-field); border: 1px solid var(--pv2-border);
	color: var(--pv2-text-soft); font-family: var(--pv2p-font);
	font-size: 12px; font-weight: 600; white-space: nowrap; text-decoration: none;
	transition: background-color .12s, border-color .12s, color .12s;
}
body.pubv2 .pv2p a.pv2p-chip:hover { background: var(--pv2-hover); border-color: var(--pv2-border-strong); color: #fff; }

/* ------------------------------------------------------------
   FORM CONTROLS — every control on a row shares ONE explicit height
   (design-system HARD RULE: never rely on padding math to line them up).
   ------------------------------------------------------------ */
body.pubv2 .pv2p-field { margin: 0 0 var(--pv2p-s3); }
body.pubv2 .pv2p-flabel {
	display: block; margin: 0 0 var(--pv2p-s1);
	font-family: var(--pv2p-font); font-size: 11px; font-weight: 600;
	letter-spacing: .06em; text-transform: uppercase; color: var(--pv2-text-muted);
}
body.pubv2 .pv2p-input,
body.pubv2 .pv2p-select,
body.pubv2 .pv2p-textarea {
	width: 100%; height: var(--pv2p-ctl); padding: 0 12px;
	background: var(--pv2-field); border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius); color: #fff; box-shadow: none;
	font-family: var(--pv2p-font); font-size: 14px; font-weight: 400;
	margin: 0; transition: border-color .12s;
}
body.pubv2 .pv2p-textarea { height: auto; min-height: 110px; padding: 10px 12px; line-height: 1.55; resize: vertical; }
body.pubv2 .pv2p-input::placeholder,
body.pubv2 .pv2p-textarea::placeholder { color: var(--pv2-text-dim); }
/* ONE focus indicator: the accent border. No outline, no halo — the same
   call the auth surface locked in for the field people actually type in. */
body.pubv2 .pv2p-input:focus,
body.pubv2 .pv2p-select:focus,
body.pubv2 .pv2p-textarea:focus { outline: none; border-color: var(--pv2-accent); box-shadow: none; }
body.pubv2 .pv2p-ctlrow { display: flex; align-items: stretch; gap: var(--pv2p-s2); flex-wrap: wrap; }
body.pubv2 .pv2p-ctlrow > * { margin: 0; }

/* THE checkbox — the product-wide standard from panel-v2-core.css, so the
   public site can never grow a second one. Never write a per-page box. */
body.pubv2 .pv2p input[type="checkbox"],
body.pubv2 .pv2p input[type="radio"] {
	-webkit-appearance: none; appearance: none;
	width: 16px; height: 16px; margin: 0; padding: 0;
	background: var(--pv2-field); border: 1px solid var(--pv2-border-strong);
	border-radius: 3px; cursor: pointer; position: relative;
	vertical-align: middle; flex-shrink: 0;
	transition: background-color .12s, border-color .12s;
}
body.pubv2 .pv2p input[type="radio"] { border-radius: 50%; }
body.pubv2 .pv2p input[type="checkbox"]:hover,
body.pubv2 .pv2p input[type="radio"]:hover { border-color: var(--pv2-accent); }
body.pubv2 .pv2p input[type="checkbox"]:checked,
body.pubv2 .pv2p input[type="radio"]:checked { background: var(--pv2-gold); border-color: var(--pv2-gold); }
/* the tick is CSS geometry, not a glyph — fa-subset carries no guarantee
   for a page that later drops the subset link. */
body.pubv2 .pv2p input[type="checkbox"]:checked::after {
	content: ''; position: absolute; left: 50%; top: 46%;
	width: 4px; height: 8px; border: solid #fff;
	border-width: 0 2px 2px 0; transform: translate(-50%, -50%) rotate(45deg);
}
body.pubv2 .pv2p input[type="radio"]:checked::after {
	content: ''; position: absolute; left: 50%; top: 50%;
	width: 6px; height: 6px; border-radius: 50%; background: #fff;
	transform: translate(-50%, -50%);
}
body.pubv2 .pv2p input[type="checkbox"]:focus-visible,
body.pubv2 .pv2p input[type="radio"]:focus-visible { outline: 2px solid rgba(213, 168, 74, .4); outline-offset: 1px; }

/* ------------------------------------------------------------
   TABLES — fixed layout + border-box, the two cures every DataTables
   right-edge bleed in this codebase came back to. On phones the table
   scrolls INSIDE its wrapper, never the page.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-tablewrap { width: 100%; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
body.pubv2 .pv2p-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
body.pubv2 .pv2p-table th,
body.pubv2 .pv2p-table td { box-sizing: border-box !important; }
body.pubv2 .pv2p-table th {
	text-align: left; padding: 10px 12px; background: var(--pv2-surface);
	font-family: var(--pv2p-font); font-size: 11px; font-weight: 600;
	letter-spacing: .06em; text-transform: uppercase; color: var(--pv2-text-muted);
	border-bottom: 1px solid var(--pv2-border);
}
body.pubv2 .pv2p-table td {
	padding: 11px 12px; font-size: 13px; color: var(--pv2-text-soft);
	border-bottom: 1px solid var(--pv2-line); vertical-align: top;
}
body.pubv2 .pv2p-table tr:last-child td { border-bottom: 0; }
body.pubv2 .pv2p-table tbody tr:hover { background: rgba(255, 255, 255, 0.02); }

/* label / value rows — the non-tabular alternative to a two-column table */
body.pubv2 .pv2p-dl { margin: 0; display: grid; gap: 0; }
body.pubv2 .pv2p-dl > div {
	display: grid; grid-template-columns: minmax(0, 200px) minmax(0, 1fr);
	gap: var(--pv2p-s2) var(--pv2p-s4); padding: 10px 0;
	border-bottom: 1px solid var(--pv2-line);
}
body.pubv2 .pv2p-dl > div:last-child { border-bottom: 0; }
body.pubv2 .pv2p-dl dt { margin: 0; font-size: 12.5px; font-weight: 600; color: var(--pv2-text-muted); }
body.pubv2 .pv2p-dl dd { margin: 0; font-size: 13.5px; color: var(--pv2-text-soft); min-width: 0; }
@media only screen and (max-width: 560px) {
	body.pubv2 .pv2p-dl > div { grid-template-columns: minmax(0, 1fr); gap: 3px; }
}

/* stat tiles are CHIPS, not banners — a 400px tile holding a 4-character
   number is 80% empty (About page finding).
   BUT a flex-wrap chip row IS the ragged wrap this contract rejects for tile
   strips: measured, a 4-up wrapped 3+1 at 1100 and a 3-up wrapped 2+1 at 768.
   The strip is now a grid on FIXED track counts stepped at breakpoints, same
   rule as .pv2p-grid, and the chip idea survives as the track width — the
   tiles stay narrow, they just stop wrapping raggedly.
   `.pv2p-stats.flow` keeps the old free-wrapping behaviour for a genuinely
   variable-length run. */
body.pubv2 .pv2p-stats {
	display: grid; gap: var(--pv2p-s2); margin: 0; padding: 0; list-style: none;
	grid-template-columns: minmax(0, 1fr);
	align-items: stretch;
}
body.pubv2 .pv2p-stats:before,
body.pubv2 .pv2p-stats:after { content: none; }
/* The ramp NEVER passes through a track count the tile count does not divide
   by — a stat strip is always an exact 2, 3 or 4, so it should never orphan
   in the first place. That is why cols-3 goes 1 -> 3 and skips 2 (a 3-up in
   2 tracks is the 2+1 that was reported), and why the orphan-span trick used
   on .pv2p-grid is NOT used here: a double-width tile holding a
   four-character number is the "80% empty" rejection this component exists
   to avoid. */
@media only screen and (min-width: 600px) {
	body.pubv2 .pv2p-stats.cols-2,
	body.pubv2 .pv2p-stats.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media only screen and (min-width: 640px) {
	body.pubv2 .pv2p-stats,
	body.pubv2 .pv2p-stats.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media only screen and (min-width: 1000px) {
	body.pubv2 .pv2p-stats.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
body.pubv2 .pv2p-stats.flow { display: flex; flex-wrap: wrap; }
body.pubv2 .pv2p-stats.flow > .pv2p-stat { min-width: 200px; }
body.pubv2 .pv2p-stat {
	/* min-width is 0 for the grid: a 200px floor on a grid item overflows a
	   narrow track instead of shrinking. .flow puts the chip floor back. */
	flex: 0 1 300px; min-width: 0; margin: 0;
	background: var(--pv2-field); border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius); padding: 11px 12px;
}
body.pubv2 .pv2p-stat b {
	display: block; font-family: var(--pv2p-font);
	font-size: 19px; font-weight: 700; line-height: 1.15; color: #fff;
	font-variant-numeric: tabular-nums;
}
body.pubv2 .pv2p-stat span { display: block; margin: 5px 0 0; font-size: 11.5px; line-height: 1.35; color: var(--pv2-text-muted); }

/* ------------------------------------------------------------
   .pv2p-table.rows — THE PHONE ROW-CARD.
   Reported as the single biggest remaining defect on the locations pages:
   a four-column server table inside a 360px rail either scrolls sideways
   inside its wrapper (so half the row is always off-screen and there is no
   affordance saying so) or squeezes IP:Port into 80px.

   Below 560 each <tr> becomes a card and each <td> a label/value row. The
   label comes from `data-label` on the <td>, which the page adds — a
   presentation attribute, and the string is the <th> that is already in the
   DOM directly above it, so no new copy enters the page.

   THE <thead> IS NOT `display: none`. It is visually hidden and left in the
   accessibility tree: hiding it outright would delete the column names for a
   screen reader, which IS a content change. Same reason `visibility:hidden`
   is load-bearing elsewhere in this sheet.

   A <td> with no data-label (an empty-state `colspan` row) renders as a
   plain full-width line, no label gutter.
   ------------------------------------------------------------ */
@media only screen and (max-width: 560px) {
	body.pubv2 .pv2p-table.rows { table-layout: auto; width: 100%; }
	body.pubv2 .pv2p-table.rows thead {
		position: absolute; width: 1px; height: 1px;
		overflow: hidden; clip-path: inset(50%); white-space: nowrap;
	}
	body.pubv2 .pv2p-table.rows tbody,
	body.pubv2 .pv2p-table.rows tr,
	body.pubv2 .pv2p-table.rows td { display: block; width: auto; }
	body.pubv2 .pv2p-table.rows tr {
		background: var(--pv2-field);
		border: 1px solid var(--pv2-border);
		border-radius: var(--pv2-radius);
		margin: 0 0 var(--pv2p-s2); padding: 3px 0;
	}
	body.pubv2 .pv2p-table.rows tr:last-child { margin-bottom: 0; }
	body.pubv2 .pv2p-table.rows tbody tr:hover { background: var(--pv2-field); }
	body.pubv2 .pv2p-table.rows td {
		display: flex; align-items: baseline; gap: var(--pv2p-s3);
		border: 0; padding: 7px 12px; font-size: 13px; min-width: 0;
	}
	body.pubv2 .pv2p-table.rows td[data-label]:before {
		content: attr(data-label);
		flex: 0 0 38%; min-width: 0;
		font-family: var(--pv2p-font);
		font-size: 10.5px; font-weight: 600; letter-spacing: .07em;
		text-transform: uppercase; color: var(--pv2-text-muted);
	}
	body.pubv2 .pv2p-table.rows td > * { min-width: 0; }
}

/* ------------------------------------------------------------
   .pv2p-map — a frame for a map or any other JS-drawn canvas the page does
   not control the internals of. It owns the border, the surface and the
   clip; it deliberately owns NO height, because the plugins that draw into
   these (jvectormap and friends) write an inline height and would win
   anyway. Pair it with a width query if the drawing is not responsive.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-map {
	position: relative; overflow: hidden;
	background: var(--pv2-surface);
	border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius-lg);
}
body.pubv2 .pv2p-map > img,
body.pubv2 .pv2p-map > svg { display: block; width: 100%; height: auto; max-width: 100%; }
body.pubv2 .pv2p-map-cap { margin: 8px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--pv2-text-dim); }

/* ------------------------------------------------------------
   .pv2p-steps / .pv2p-step — a numbered "how it works" run.
   The badge takes whatever the page puts in .pv2p-step-n, so the numeral
   stays real markup. `.pv2p-steps.autonum` generates it from a CSS counter
   instead, for a page that genuinely has no numeral to keep — use that only
   where no number exists today, never to replace one that does.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-steps {
	display: grid; gap: var(--pv2p-s5);
	grid-template-columns: minmax(0, 1fr);
	margin: 0; padding: 0; list-style: none;
	counter-reset: pv2pstep;
}
body.pubv2 .pv2p-steps:before,
body.pubv2 .pv2p-steps:after { content: none; }
@media only screen and (min-width: 700px) {
	body.pubv2 .pv2p-steps,
	body.pubv2 .pv2p-steps.cols-2,
	body.pubv2 .pv2p-steps.cols-3,
	body.pubv2 .pv2p-steps.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media only screen and (min-width: 1000px) {
	body.pubv2 .pv2p-steps,
	body.pubv2 .pv2p-steps.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
	body.pubv2 .pv2p-steps.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
body.pubv2 .pv2p-step { position: relative; margin: 0; padding: 0 0 0 48px; min-width: 0; }
body.pubv2 .pv2p-step-n {
	position: absolute; left: 0; top: 0;
	display: inline-flex; align-items: center; justify-content: center;
	width: 34px; height: 34px; border-radius: 50%;
	background: var(--pv2-field); border: 1px solid var(--pv2-border-strong);
	color: var(--pv2-gold);
	font-family: var(--pv2p-font); font-size: 13px; font-weight: 700; line-height: 1;
	font-variant-numeric: tabular-nums;
}
body.pubv2 .pv2p-steps.autonum > .pv2p-step:before {
	counter-increment: pv2pstep; content: counter(pv2pstep);
	position: absolute; left: 0; top: 0;
	display: inline-flex; align-items: center; justify-content: center;
	width: 34px; height: 34px; border-radius: 50%;
	background: var(--pv2-field); border: 1px solid var(--pv2-border-strong);
	color: var(--pv2-gold);
	font-family: var(--pv2p-font); font-size: 13px; font-weight: 700; line-height: 1;
}
body.pubv2 .pv2p-step > h1,
body.pubv2 .pv2p-step > h2,
body.pubv2 .pv2p-step > h3,
body.pubv2 .pv2p-step > h4,
body.pubv2 .pv2p-step > h5,
body.pubv2 .pv2p-step > h6,
body.pubv2 .pv2p-step-title {
	margin: 6px 0 var(--pv2p-s1); padding: 0; border: 0;
	font-family: var(--pv2p-font); font-size: 15px; font-weight: 600;
	line-height: 1.35; color: #fff; text-transform: none; letter-spacing: 0;
}
body.pubv2 .pv2p-step p { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--pv2-text-muted); }

/* ------------------------------------------------------------
   .pv2p-icon — the icon chip an "icon feature" card is built from. It is
   NOT a component of its own: compose it with .pv2p-card + .pv2p-cbody, or
   with .pv2p-step. Font-style is forced normal because `i` is both the
   italic element and Font Awesome's carrier tag on this site.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-icon {
	display: inline-flex; align-items: center; justify-content: center;
	width: 38px; height: 38px; flex-shrink: 0;
	border-radius: var(--pv2-radius);
	background: var(--pv2-field); border: 1px solid var(--pv2-border);
	color: var(--pv2-accent); font-size: 16px; line-height: 1;
	margin: 0 0 var(--pv2p-s3);
}
body.pubv2 .pv2p-icon.round { border-radius: 50%; }
body.pubv2 .pv2p-icon.gold { background: rgba(234, 168, 36, 0.10); border-color: rgba(234, 168, 36, 0.32); color: var(--pv2-gold); }
body.pubv2 .pv2p-icon i,
body.pubv2 .pv2p-icon em { font-style: normal; line-height: 1; }

/* ------------------------------------------------------------
   LISTS — .pv2p-list is the marker-free list. `.check` draws its tick
   with CSS borders. The theme's ul.list-icon asks for `font-family:
   FontAwesome` (FA4) with weight 400 — a family the public site does not
   load at all, so those bullets render as NOTHING today. Anywhere
   .pv2p-list.check is applied over that markup it also repairs it.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-list { margin: 0 0 var(--pv2p-s3); padding: 0; list-style: none; }
body.pubv2 .pv2p-list > li {
	position: relative; margin: 0 0 var(--pv2p-s2); padding: 0 0 0 24px;
	font-size: 13.5px; line-height: 1.55; color: var(--pv2-text-soft);
}
body.pubv2 .pv2p-list > li:last-child { margin-bottom: 0; }
body.pubv2 .pv2p-list > li:before { content: none; }        /* kill the theme's dead FA4 bullet */
body.pubv2 .pv2p-list.check > li:after {
	content: ''; position: absolute; left: 3px; top: 6px;
	width: 5px; height: 9px; border: solid var(--pv2-accent);
	border-width: 0 2px 2px 0; transform: rotate(45deg);
}
body.pubv2 .pv2p-list.dot > li:after {
	content: ''; position: absolute; left: 4px; top: 8px;
	width: 5px; height: 5px; border-radius: 50%; background: var(--pv2-text-dim);
}
body.pubv2 .pv2p-list.plain > li { padding-left: 0; }
body.pubv2 .pv2p-list.inline { display: flex; flex-wrap: wrap; gap: var(--pv2p-s2) var(--pv2p-s4); }
body.pubv2 .pv2p-list.inline > li { margin: 0; }

/* ------------------------------------------------------------
   ACCORDION — rides the theme's existing div.toggle markup and its
   jQuery (_toggle() in js/scripts.js), which slideDown/slideUp the
   .toggle-content and flips .active on the item.

   Two consequences that are load-bearing:
     - NEVER set `display` on .toggle-content. The animation writes an
       inline display and an !important here would freeze a panel open.
     - The open/closed state class is the theme's `.active`, not one of
       ours, so the chevron and the gold edge key off `.active`.

   The chevron is drawn from borders. fa-subset ships no chevron-down /
   angle-down / plus / minus at all, so a glyph here would render as
   nothing on the live site and nobody would see it in a diff.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-acc {
	margin: 0; background: var(--pv2-panel);
	border: 1px solid var(--pv2-border); border-radius: var(--pv2-radius-lg);
	box-shadow: var(--pv2p-shadow); overflow: hidden;
}
body.pubv2 .pv2p-acc-item { margin: 0; position: relative; clear: none; border-top: 1px solid var(--pv2-line); }
body.pubv2 .pv2p-acc > .pv2p-acc-item:first-child { border-top: 0; }
body.pubv2 .pv2p-acc-item > label {
	position: relative; display: flex; align-items: center; gap: var(--pv2p-s3);
	margin: 0; padding: 14px 46px 14px var(--pv2p-pad); min-height: 52px;
	background: transparent; border: 0; border-bottom: 0; border-radius: 0;
	color: #fff; font-family: var(--pv2p-font);
	font-size: 14.5px; font-weight: 600; line-height: 1.45;
	cursor: pointer; user-select: none;
	transition: background-color .12s, color .12s;
}
body.pubv2 .pv2p-acc-item > label:hover { background: rgba(255, 255, 255, 0.03); color: var(--pv2-gold); }
body.pubv2 .pv2p-acc-item > label:focus-visible { outline: 2px solid rgba(213, 168, 74, .45); outline-offset: -2px; }
/* the theme draws its own CSS triangle on both states — remove both */
body.pubv2 .pv2p-acc-item > label:before {
	content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
	border: 0; margin: 0; right: auto;
	background: transparent; transition: background-color .14s;
}
/* THE OPEN EDGE RUNS THE FULL HEIGHT OF THE OPEN ITEM, not just its
   question row. It used to be a 3px bar on the label's :before, which
   stopped at the label's bottom edge — so an open panel read as a gold
   question with an unrelated paragraph under it rather than as one
   object. Ryan on the game-page copy of this component: "I don't like the
   way it looks." An inset shadow, not a border-left: a border would shift
   every label 3px right on open and the rows would twitch. The label's
   :before stays declared and transparent because it is the THEME's
   triangle being suppressed — deleting the rule brings the triangle back. */
body.pubv2 .pv2p-acc-item.active { box-shadow: inset 3px 0 0 var(--pv2-gold); }
body.pubv2 .pv2p-acc-item.active > label:before { background: transparent; border: 0; margin: 0; }
body.pubv2 .pv2p-acc-item.active > label { color: var(--pv2-gold); background: rgba(234, 168, 36, 0.045); }
/* the chevron */
body.pubv2 .pv2p-acc-item > label:after {
	content: ''; position: absolute; right: 21px; top: 50%;
	width: 8px; height: 8px; margin-top: -6px;
	border: solid var(--pv2-text-muted); border-width: 0 2px 2px 0;
	transform: rotate(45deg); transform-origin: 50% 50%;
	transition: transform .18s ease, border-color .12s;
}
body.pubv2 .pv2p-acc-item.active > label:after { border-color: var(--pv2-gold); margin-top: -2px; transform: rotate(225deg); }
/* THE ANSWER'S TOP PADDING WAS 2px AGAINST A BOTTOM OF 18px. Measured on
   /services/game_servers/7_days_to_die/ and identical here: `2px 18px
   18px`, so the answer's first line sat 2px under the question row it
   belongs to and then floated 18px clear of the card's bottom edge. Ryan:
   "The top margin where the text reads is really weird." The asymmetry
   was the bug — an answer has to breathe from its question. 14/18 now,
   the bottom still carrying the card's closing edge. */
body.pubv2 .pv2p-acc-item > .toggle-content,
body.pubv2 .pv2p-acc-body {
	margin: 0; padding: var(--pv2p-s3) var(--pv2p-pad) var(--pv2p-s4);
	background: transparent; border: 0; border-radius: 0;
	font-size: var(--pv2p-fs-body); line-height: var(--pv2p-lh-body); color: var(--pv2-text-soft);
}
body.pubv2 .pv2p-acc-item > .toggle-content > *:last-child { margin-bottom: 0; }
@media (prefers-reduced-motion: reduce) {
	body.pubv2 .pv2p-acc-item > label:after { transition: none; }
}

/* ------------------------------------------------------------
   HERO + MEDIA
   ------------------------------------------------------------ */
body.pubv2 .pv2p-hero {
	position: relative; overflow: hidden;
	background: var(--pv2-panel); border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius-lg); box-shadow: var(--pv2p-shadow);
	padding: var(--pv2p-s6) var(--pv2p-pad);
}
body.pubv2 .pv2p-hero:before {
	content: ''; position: absolute; inset: 0; pointer-events: none;
	background:
		radial-gradient(420px 220px at 100% -20%, rgba(234, 168, 36, 0.14), rgba(234, 168, 36, 0) 70%),
		var(--pv2p-weave);
}
body.pubv2 .pv2p-hero > * { position: relative; }
body.pubv2 .pv2p-hero-title {
	margin: 0 0 var(--pv2p-s2); padding: 0; border: 0;
	font-family: var(--pv2p-font); font-size: 24px; font-weight: 600;
	line-height: 1.25; color: #fff; text-transform: none;
}
body.pubv2 .pv2p-hero-sub { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--pv2-text-muted); }

/* figure + copy. Two columns above 760, picture FIRST when it stacks —
   a figure stranded under its own body copy reads as an afterthought. */
body.pubv2 .pv2p-media { display: grid; gap: var(--pv2p-s4); align-items: start; grid-template-columns: minmax(0, 1fr); }
@media only screen and (min-width: 760px) {
	body.pubv2 .pv2p-media { grid-template-columns: minmax(0, 1fr) clamp(250px, 40%, 420px); gap: var(--pv2p-s4) var(--pv2p-s5); }
	body.pubv2 .pv2p-media.rev { grid-template-columns: clamp(250px, 40%, 420px) minmax(0, 1fr); }
	body.pubv2 .pv2p-media.rev > .pv2p-fig { order: -1; }
}
body.pubv2 .pv2p-fig { margin: 0; padding: 0; min-width: 0; }
body.pubv2 .pv2p-fig img {
	display: block; width: 100%; height: auto; max-width: 100%;
	border: 1px solid var(--pv2-border); border-radius: var(--pv2-radius);
	background: var(--pv2-surface);
}
body.pubv2 .pv2p-fig figcaption { margin: 8px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--pv2-text-dim); font-style: normal; }
/* .bare — a cut-out render on transparency (a product shot, a logo, a
   character PNG) does not want a hairline box drawn around its bounding
   rectangle and a surface fill behind it. Screenshots keep the frame. */
body.pubv2 .pv2p-fig.bare img { border: 0; background: transparent; border-radius: 0; }

/* ------------------------------------------------------------
   SLIDE DOTS — paging for a carousel or a hero rotator.
   THE HIT RULE, third case: this is a horizontal run, so the row cannot
   grow (the strip is centred under the slide and the dots would push each
   other apart). Each dot keeps a 9px MARK and takes a 24 x 44 hit CELL —
   the cells tile edge to edge with no overlap, so no dot can fire its
   neighbour, and the vertical band is the full 44. Same call as the footer
   socials, opposite call to the footer link list.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-dots {
	display: flex; align-items: center; justify-content: center;
	gap: 0; margin: 0; padding: 0; list-style: none;
}
body.pubv2 .pv2p-dots > li { margin: 0; padding: 0; }
body.pubv2 .pv2p-dot,
body.pubv2 a.pv2p-dot,
body.pubv2 button.pv2p-dot {
	display: inline-flex; align-items: center; justify-content: center;
	width: 24px; height: 44px; padding: 0; margin: 0;
	background: transparent; border: 0; cursor: pointer;
	-webkit-appearance: none; appearance: none;
	font-size: 0; line-height: 0; text-indent: 0; overflow: hidden;
}
body.pubv2 .pv2p-dot:before {
	content: ''; display: block;
	width: 9px; height: 9px; border-radius: 50%;
	background: rgba(255, 255, 255, 0.30);
	box-shadow: none;
	transition: background-color .14s, transform .14s;
}
body.pubv2 .pv2p-dot:hover:before { background: rgba(255, 255, 255, 0.55); }
body.pubv2 .pv2p-dot.active:before,
body.pubv2 .pv2p-dot[aria-current="true"]:before { background: var(--pv2-gold); }
body.pubv2 .pv2p-dot:focus-visible { outline: 2px solid rgba(213, 168, 74, .45); outline-offset: -6px; border-radius: 3px; }
@media only screen and (max-width: 759px) {
	/* picture first when it stacks. The 480px cap stops a wide screenshot
	   from dwarfing the copy under it — and it has to centre, or the
	   figure sits left while the block around it reads as a unit. Same
	   class of bug as the callout lede; found in the same audit. */
	body.pubv2 .pv2p-media > .pv2p-fig { order: -1; max-width: 480px; margin-left: auto; margin-right: auto; }
}

/* ------------------------------------------------------------
   CTA — the closing strip. Replaces the theme's .callout, which paints a
   flat brand-colour band with white text and a bootstrap button.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-cta {
	display: flex; align-items: center; gap: var(--pv2p-s5); flex-wrap: wrap;
	background: var(--pv2-panel); border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius-lg); padding: 20px var(--pv2p-pad);
	margin: 0;
}
body.pubv2 .pv2p-cta-txt { flex: 1 1 300px; min-width: 0; }
body.pubv2 .pv2p-cta-txt > h1,
body.pubv2 .pv2p-cta-txt > h2,
body.pubv2 .pv2p-cta-txt > h3,
body.pubv2 .pv2p-cta-txt > h4 {
	margin: 0; padding: 0; border: 0;
	font-family: var(--pv2p-font); font-size: 18px; font-weight: 600;
	line-height: 1.3; color: #fff; text-transform: none;
}
body.pubv2 .pv2p-cta-txt p { margin: 6px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--pv2-text-muted); }
body.pubv2 .pv2p-cta .pv2p-btn { flex: 0 0 auto; }

/* the theme's .callout, when a page keeps that wrapper for layout. Kill
   the brand band and let .pv2p-cta inside it carry the design. */
body.pubv2 .pv2p-callout { background: transparent; padding: 0; margin: 0; border: 0; }

/* ------------------------------------------------------------
   EMPTY STATE
   ------------------------------------------------------------ */
body.pubv2 .pv2p-empty { text-align: center; padding: 46px 20px; color: var(--pv2-text-muted); }
body.pubv2 .pv2p-empty strong { display: block; color: var(--pv2-text-soft); font-size: 15px; font-weight: 600; margin-bottom: 5px; }

/* ============================================================
   RESPONSIVE — mobile FIRST is the default above; these are the two
   steps up and the phone comfort pass. Nothing reflows into a different
   layout at any breakpoint: the phone gets the same design narrowed.
   ============================================================ */

/* the widened rail. Measure and TYPE grow together — growing the box
   without growing the type is how you end up with a 100-character line. */
@media only screen and (min-width: 1400px) {
	/* Declared on body.pubv2, NOT on .pv2p — the band has to resolve the
	   same ramp or the two rails disagree (see the band rule above). These
	   are tokens: nothing paints until a .pv2p-* component consumes them. */
	body.pubv2 {
		--pv2p-fs-body: 15.5px;
		--pv2p-lh-body: 1.66;
		--pv2p-pad: var(--pv2p-s5);
	}
	body.pubv2 .pv2p-chead { padding: 12px var(--pv2p-s5); }
	body.pubv2 .pv2p-hero { padding: var(--pv2p-s7) var(--pv2p-s5); }
	body.pubv2 .pv2p-cta { padding: 22px var(--pv2p-s5); }
	body.pubv2 .pv2p-acc-item > label { padding-left: var(--pv2p-s5); font-size: 15px; }
	body.pubv2 .pv2p-acc-item > .toggle-content { padding-left: var(--pv2p-s5); padding-right: var(--pv2p-s5); }
}

/* tablet / small laptop */
@media only screen and (max-width: 900px) {
	body.pubv2 .pv2p { padding-top: var(--pv2p-s6); }
}

/* phone comfort */
@media only screen and (max-width: 560px) {
	body.pubv2 {
		--pv2p-pad: var(--pv2p-s3);
		--pv2p-s5: 20px;
		--pv2p-s6: 28px;
		--pv2p-s7: 32px;
	}
	body.pubv2 .pv2p-band { padding: 18px 0 16px; }
	body.pubv2 .pv2p-band h1,
	body.pubv2 .pv2p-band h2,
	body.pubv2 .parallax.parallax-1:not(.text-center) h1,
	body.pubv2 .parallax.parallax-1:not(.text-center) h2,
	body.pubv2 section.page-title:not(.pv2p-band) h1,
	body.pubv2 section.page-title:not(.pv2p-band) h2 { font-size: 22px; }
	body.pubv2 .pv2p-doc .pv2p-cbody { padding: var(--pv2p-s4) var(--pv2p-s3); }
	body.pubv2 .pv2p-acc-item > label { padding: 13px 42px 13px var(--pv2p-s3); font-size: 14px; }
	body.pubv2 .pv2p-acc-item > .toggle-content { padding-left: var(--pv2p-s3); padding-right: var(--pv2p-s3); }
	body.pubv2 .pv2p-cta { flex-direction: column; align-items: stretch; gap: var(--pv2p-s3); text-align: left; }
	body.pubv2 .pv2p .pv2p-btn { width: 100%; }
	body.pubv2 .pv2p .pv2p-btn.sm { width: auto; }
	/* .block is the explicit opt-in for a full-width button OUTSIDE a shell —
	   a hero CTA must not go full-bleed just because it is on a phone. */
	body.pubv2 .pv2p-stat { flex: 1 1 100%; min-width: 0; }
	body.pubv2 .pv2p-stats.flow > .pv2p-stat { flex: 1 1 100%; }
	body.pubv2 .pv2p-hero { padding: var(--pv2p-s5) var(--pv2p-s3); }
	body.pubv2 .pv2p-hero-title { font-size: 21px; }
}

/* ============================================================
   TOUCH HIT FLOOR — 44px, coarse pointers plus the 900px width mirror.
   Headless Chromium drops `hover: none` after the first navigation in a
   Playwright context, so a pointer-only rule ships unverified; the panel
   files carry the same doubled query for the same reason.

   Everything here either grows a control that owns its own row, or adds
   an invisible hit extender. No layout moves and nothing can widen the
   page at 390.
   ============================================================ */
@media (hover: none) and (pointer: coarse), (max-width: 900px) {
	body.pubv2 .pv2p-btn { min-height: 44px; }
	body.pubv2 .pv2p-acc-item > label { min-height: 48px; }
	/* inline prose links: extend the tap band vertically only. A wide
	   extender in running text would steal the neighbouring line's taps. */
	body.pubv2 .pv2p-list > li > a,
	body.pubv2 .pv2p-chip { position: relative; }
	body.pubv2 .pv2p-list > li > a:after,
	body.pubv2 .pv2p-chip:after {
		content: ''; position: absolute; left: 0; right: 0; top: 50%;
		height: 44px; transform: translateY(-50%); pointer-events: auto;
	}
	body.pubv2 .pv2p-chip { height: 32px; }
	/* the 16px product-standard checkbox must not grow — it gets the tap
	   area instead, on ::before (the tick owns ::after). */
	body.pubv2 .pv2p input[type="checkbox"]:before,
	body.pubv2 .pv2p input[type="radio"]:before {
		content: ''; position: absolute; left: 50%; top: 50%;
		width: 44px; height: 44px; transform: translate(-50%, -50%); pointer-events: auto;
	}
	body.pubv2 .pv2p-input,
	body.pubv2 .pv2p-select,
	body.pubv2 .pv2p-textarea { --pv2p-ctl: 44px; font-size: 16px; } /* 16px: iOS zooms a smaller field on focus */
}

/* ============================================================
   #toTop — shared public chrome, same finding and same fix as the two
   panels (panel-v2-core.css / deathstar-v2-core.css). The theme ships a
   48x35 opaque grey slab pinned to the bottom edge with square corners
   and no shadow; being fixed it paints over whatever row happens to be
   at the bottom of the viewport and reads as a broken box.

   Owned here so no public page re-solves it. It is chrome, not a page
   component, which is why it is scoped to body.pubv2 rather than .pv2p.
   ============================================================ */
@media (max-width: 900px) {
	body.pubv2 #toTop {
		width: 44px; height: 44px; right: 12px; bottom: 14px;
		padding: 0; line-height: 44px; border-radius: 50%;
		background: rgba(33, 33, 34, 0.94);
		border: 1px solid var(--pv2-border-strong);
		box-shadow: 0 6px 20px rgba(0, 0, 0, 0.6);
		color: var(--pv2-text-soft); opacity: 1;
	}
	body.pubv2 #toTop:before { font-size: 15px; line-height: inherit; }
}

/* ============================================================
   ============================================================
   PART 3 — THE SHARED VOCABULARY LAYER   ** HIGH BLAST RADIUS **
   ============================================================

   Measured across the 328 public templates (home-page audit, 2026-08-04):

       .callout           304 templates
       .flexslider        318 templates
       section.alternate  300 templates
       ul.list-icon       296 templates

   These four ARE the public site. Everything else is page furniture.
   That cuts both ways and this section is written for both edges:

     UPSIDE — a good rule here is inherited by ~300 game pages with
     nobody editing a template. It is the difference between this
     project taking days and taking weeks.

     DANGER — there is no such thing as a small change to .callout. Any
     edit below moves ~300 pages on one deploy, and today it moves every
     allowlisted page the moment a staff member signs in. Treat this
     section as production infrastructure: conservative changes only,
     no structural rules, and re-measure the four text pages plus
     /affiliates/ and /locations/<city>/ after ANY edit.

   WHAT IS DELIBERATELY NOT HERE
     .flexslider — a jQuery-initialised carousel whose slide widths,
     clones and nav are written by the plugin at runtime. Restyling it
     from a shared sheet means fighting inline styles the plugin owns.
     It needs a page-scoped skin by whoever takes the carousel pages,
     not a sitewide rule. Do not add one here.

   THE INVISIBLE-CONTENT TRAP (read before touching any band)
     essentials.css ships `.animate_fade_in { opacity: 0 }` and the
     .animate_from_top / _bottom / _left / _right siblings, plus
     `[data-animation]` blocks. NOTHING in CSS ever turns them back on:
     js/scripts.js _animate() reveals them with jQuery .appear() +
     .animate({opacity:1}). Three consequences:
       - the [data-animation] path only runs above 767px wide;
       - an element that never scrolls into view stays at opacity 0;
       - drop or rename one of those classes in a template and its
         content is gone with no error anywhere.
     If a redesign removes the class it must ALSO stop relying on the
     JS — set opacity to 1 in your page scope in the same commit.

   Everything below is a colour/type change on markup that already
   exists. No layout property, no display, no position, no width.
   ============================================================ */

/* ------------------------------------------------------------
   3.0  THE SURFACE LADDER — why the site read as "stale" and "grey".

   Ryan on /services/game_servers/7_days_to_die/: "I hate the way this
   looks. It looks really stale. Also, I hate the background. It has like
   a cool design, but it's usurped by the overall gray feeling of the
   website."

   MEASURED, AND THE LADDER WAS UPSIDE DOWN:

     body / #wrapper (layout-dark.css:9)   #2d2d2d   (45,45,45)  LIGHTEST
     section (no class)                    inherits  #2d2d2d
     section.alternate                     #252526
     .callout.dark                         #252526
     section.ss-wg                         #1a1a1b

   The PAGE was the lightest surface on it, and every band sat DARKER —
   the exact inverse of the panel, where `--pv2-surface` #1a1a1b is the
   page and every panel steps UP from it. With the page that light the
   whole ramp is compressed into 20 RGB units at the pale end, which is
   what "overall gray feeling" is: there is no black anywhere for the gold
   and the artwork to sit against, and the `.callout` weave and bloom Ryan
   likes ("it has like a cool design") are washed out by the surface
   behind them.

   Worse, two of the values were IDENTICAL AND ADJACENT: `section.alternate`
   immediately followed by `.callout.dark`, both #252526, reading as one
   continuous field with no boundary at all. That is the "adjacent
   sections read as one grey field" report.

   THE LADDER NOW — the panel's, by token, not by hex:

     .callout (CTA band)       --pv2p-deep    #131314   one rung BELOW
     body / #wrapper           --pv2-surface  #1a1a1b   the page
     section (no class)        inherits       #1a1a1b
     section.alternate         --pv2-panel    #212122   alternating band
     .callout.dark             --pv2-raised   #252526   emphasis band
     section.ss-wg             --pv2-panel    #212122   (was the page
                                                         colour, which
                                                         now merges)

   The CTA rung was added 2026-08-05 by the rhythm sweep: the plain
   .callout used to share the page's #1a1a1b, which the 323-page crawl
   measured as 711 same-fill adjacencies on 289 pages. See the rule and
   the rhythm guard further down.

   Plus a hairline at the top of every banded section: a 7-unit step is a
   soft edge, and the panel draws its surfaces with borders, not with
   colour alone. Inset shadow rather than border-top — a border adds a
   pixel to the box and pushes every band one pixel down the page.

   THIS TOUCHES NO CARD, NO PRICING TIER AND NO TEXT COLOUR. .pv2p-card
   was already --pv2-panel on a lighter page, i.e. a card DARKER than the
   page under it; it is unchanged and now reads correctly as raised. The
   order widget's four tier cards are untouched — see 1b.
   ------------------------------------------------------------ */
body.pubv2,
body.pubv2 #wrapper { background-color: var(--pv2-surface); }
/* THE PAGE FIELD CARRIES THE WEAVE. This is the "plain dark gray background
   that looks boring, bland" note: the largest surface on every page — 1600
   x 9982 on /terms/, measured — was a single flat fill, while the one
   treatment on the page that Ryan actually likes was rationed out to a CTA
   band. The default is now the other way round: the field is woven, and a
   surface has to opt OUT by painting its own opaque fill.

   On #wrapper and not on body, because both are painted --pv2-surface and
   #wrapper spans the full document height — putting it on both would stack
   the gradient twice and double the alpha wherever they overlap. body keeps
   the flat fill so the overscroll gutter still matches. */
body.pubv2 #wrapper { background-image: var(--pv2p-weave); }
/* The banded sections paint their own opaque fill OVER #wrapper, so each one
   would have punched a flat rectangle out of the woven field. They take the
   weave with them and the page reads as one material. */
body.pubv2 section.ss-wg { background-color: var(--pv2-panel); }
body.pubv2 section.alternate,
body.pubv2 section.ss-wg { background-image: var(--pv2p-weave); }
/* `section.alternate` takes the same rung, declared with the rest of that
   class further down so there is one rule per class, not two. */
body.pubv2 section.alternate,
body.pubv2 section.ss-wg { box-shadow: inset 0 1px 0 var(--pv2-line); }
/* the page-title band gets BOTH edges. Its fill is the theme's and on
   /about/ it is opaque --pv2-surface, i.e. exactly the page colour now —
   without a bottom rule the band would have no edge at all there. */
body.pubv2 section.page-title {
	box-shadow: inset 0 1px 0 var(--pv2-line), inset 0 -1px 0 var(--pv2-line);
}

/* ---------- .callout — the CTA band ----------
   Today: a full-bleed brand-gold band (.styleBackgroundColor takes its
   fill from color_scheme/yellow.css `.styleBackgroundColor`, 0-1-0) with
   white text and a ghost-outlined button. A solid gold band is the same
   "way too bright" note the gold-button stack got, and it is the only
   light-coloured element left on a dark page.

   v2: the band becomes deep chrome with a gold bloom drifting in from
   the right, and the gold is spent on the ONE button instead. Structure,
   grid and padding are untouched. */
/* The bloom is LAYERED BACKGROUNDS on the band itself, deliberately not an
   absolutely-positioned ::before with `.callout > * { z-index: 1 }`. That
   pattern would stamp a stacking context onto whatever wrapper a band
   happens to contain, and a stacking context above a payment form is how
   you make a Stripe modal invisible with no error anywhere (order-flow
   audit rule 3). Backgrounds cost nothing and trap nothing. */
/* THE FILL IS ONE RUNG BELOW THE PAGE, NOT THE PAGE'S OWN. The first
   version painted this --pv2-surface, and the crawl measured the cost:
   the CTA band sat against a plain section (same #1a1a1b) on 711
   adjacencies across 289 of 323 public pages — hairlines and bloom on an
   identical fill still read as one slab, which is the "no establishment
   of order" report. --pv2p-deep is the band's own rung: darker stage,
   gold bloom pops harder, and by construction it can only ever sit
   flush against another CTA band (the deliberate ARK/Space Engineers
   platform stacks). .callout.dark re-declares its own fill below and is
   untouched. */
body.pubv2 .callout {
	background-color: var(--pv2p-deep);
	background-image: var(--pv2p-bloom), var(--pv2p-weave);
	border-top: 1px solid var(--pv2-line);
	border-bottom: 1px solid var(--pv2-line);
}
body.pubv2 .callout h2,
body.pubv2 .callout h3,
body.pubv2 .callout h4,
body.pubv2 .callout.styleBackgroundColor h2,
body.pubv2 .callout.styleBackgroundColor h3,
body.pubv2 .callout.styleBackgroundColor h4 {
	font-family: var(--pv2p-font);
	/* 400, not 600 — see the weight note in PART 5.4. A band head is the
	   loudest line in ITS band, which colour already says; adding weight on
	   top of that is what made the whole page read as bold. */
	font-size: 20px; font-weight: 400; line-height: 1.35; color: #fff;
	margin: 0 0 6px; text-transform: none; letter-spacing: .005em;
}
body.pubv2 .callout p,
body.pubv2 .callout.styleBackgroundColor p {
	font-family: var(--pv2p-font);
	font-size: 14.5px; font-weight: 400; line-height: 1.55;
	color: var(--pv2-text-muted); margin: 0; padding: 0;
}
/* .callout.dark keeps its own darker fill; retint the arrow pseudo-
   elements with it or the notch reads as a stray black triangle. */
body.pubv2 .callout.dark { background-color: var(--pv2-raised); }
/* layout.css:252 paints `#252525` on the dark callout's HEADINGS AND
   PARAGRAPHS, not just on the band:
       .callout.dark, .callout.dark h2, h3, h4, p { background-color:#252525 }
   Every one of those is an opaque strip the exact width of its line box,
   punched through whatever is behind it — a visible dark rectangle across
   the gold bloom behind every dark-callout heading on ~300 templates, and
   an absolute block on putting any raised surface inside a dark callout.
   The band keeps its fill; its text does not get one. */
body.pubv2 .callout.dark h2,
body.pubv2 .callout.dark h3,
body.pubv2 .callout.dark h4,
body.pubv2 .callout.dark h5,
body.pubv2 .callout.dark h6,
body.pubv2 .callout.dark p { background-color: transparent; }
body.pubv2 .callout.dark p { color: var(--pv2-text-muted); max-width: 960px; }

/* ---- A CAPPED MEASURE INSIDE A CENTRED BAND MUST CENTRE ITSELF. ----

   MEASURED ON /services/game_servers/7_days_to_die/ AT 1920, and the same
   on all 288 files that ship this band:

     H2 "Our 7 Days to Die server hosting plans..."  195 -> 1725, centre 960
     P  "Your game server will come with..."         195 -> 1155, centre 675

   The band is `.container.text-center`, so the heading centres across the
   full 1530 rail. The lede under it carries the 960px cap directly above
   and `margin: 0`, so the BOX was pinned left and the text centred inside
   the box — leaving the two lines 285px out of alignment with each other.
   Ryan: "look at that second line... It's off alignment. It looks really
   bad." It was invisible while the rail was 1170 and the cap barely bit;
   widening the rail to 1560 is what exposed it.

   Auto margins, not a cap removal: a 1530px line of 14.5px copy is 175
   characters and the cap is the right call. It just has to centre.

   AUDIT OF EVERY OTHER SYSTEM-IMPOSED CAP IN THIS FILE, done at the same
   time because one missing auto-margin means there are others:
     .pv2p-doc                 margin: 0 auto            OK
     .css-hero-inner           margin: 0 auto (theme)    OK
     .pv2p > .container        margin-left/right: auto   OK
     section > header > h4/h5  96ch, LEFT-aligned header — correct pinned
                               left; the h2 above it is left-aligned too
                               and they share an edge at x=195.
     .pv2p-shead > p           78ch, left-aligned component, no centred
                               usage in any template. Correct as-is.
     .pv2p-media > .pv2p-fig   480px when the figure stacks above the copy
                               below 760 — that one WAS pinned left in a
                               block that reads as centred. Fixed below. */
body.pubv2 .callout > .container.text-center > p {
	margin-left: auto; margin-right: auto;
}
body.pubv2 .callout.dark.arrow-down:after { border-top-color: var(--pv2-raised); }
body.pubv2 .callout.dark.arrow-up:after { border-bottom-color: var(--pv2-raised); }

/* the band's button. .btn-primary is the ONE gold thing in the band;
   everything else in it is dark chrome. Height is pinned, never padding
   math (design-system HARD RULE). */
body.pubv2 .callout .btn,
body.pubv2 .callout.styleBackgroundColor .btn {
	display: inline-flex; align-items: center; justify-content: center;
	height: var(--pv2p-ctl); padding: 0 22px; margin-top: 0;
	background: transparent; border: 1px solid var(--pv2-border-strong);
	border-radius: var(--pv2-radius); color: #fff;
	font-family: var(--pv2p-font); font-size: 14px; font-weight: 400;
	line-height: 1; text-shadow: none; box-shadow: none; white-space: nowrap;
	transition: background-color .12s, border-color .12s, color .12s;
}
body.pubv2 .callout .btn:hover,
body.pubv2 .callout .btn:focus { background: var(--pv2-hover); color: #fff; }
body.pubv2 .callout .btn.btn-primary,
body.pubv2 .callout.styleBackgroundColor .btn.btn-primary {
	background: var(--pv2-gold); border-color: var(--pv2-gold);
	color: #fff; font-weight: 600;
}
body.pubv2 .callout .btn.btn-primary:hover,
body.pubv2 .callout .btn.btn-primary:focus {
	background: var(--pv2-gold-deep); border-color: var(--pv2-gold-deep); color: #fff;
}
/* Ryan, repeatedly: "Order button issue. I've mentioned this so many times
   before. It needs to be over to the right."

   MEASURED on /services/game_servers/minecraft/ at 1600: the band's rail runs
   105 -> 1510, and the button ended at x=1276. 234px of dead space to its right,
   because the button sits in a bare `.col-md-3` and inherits the row's left
   alignment — so it reads as floating in the middle of the band instead of
   anchored to the page edge. The heading is NOT mis-railed: it measures x=105,
   the same pixel as the feature band above it.

   `:has(> .btn)` keeps this off any band whose last column holds copy rather
   than a control — text-align is the whole fix, no layout property is touched,
   which is the Part 3 contract. */
body.pubv2 .callout .row > [class*="col-"]:last-child:not(:only-child):has(> .btn) {
	text-align: right;
}
@media only screen and (max-width: 990px) {
	/* stacked: the button is full width here, so right-alignment would only
	   shift its label */
	body.pubv2 .callout .row > [class*="col-"]:last-child:not(:only-child):has(> .btn) {
		text-align: center;
	}
}
/* the .btn-lg the bands use is 100% width below 990 in the theme; keep the
   full-width tap on a phone and clear the 44px floor.

   `width: 100%` ON ITS OWN NEVER LANDED, and this is the trap:
   mobile-responsive.css §1.18 ships
       @media (max-width:600px) { .col-sm-3.col-md-3,
         .col-md-3:not(.col-sm-6):not(.col-sm-12) { width:50% !important } }
   aimed at the ARK mod-tile grid. The callout's button column is a bare
   `.col-md-3` with no `col-sm-*`, so it matches, and a 100%-wide button in a
   50%-wide column measured 185px at 390 — not full width, and it looked
   deliberate. §1.18 also takes the column to 33.333% at <=990, which is why
   the CTA read as a stub on tablets.

   The column is widened THROUGH .callout, never as a bare `.col-md-3` rule:
   `.col-md-3` is one of the order page's JS hooks and a bare rule on it is
   forbidden. Both sides carry !important, so specificity decides — this is
   (0,4,1) against §1.18's (0,3,0). */
@media only screen and (max-width: 990px) {
	body.pubv2 .callout .row > .col-md-3 {
		width: 100% !important;
		float: none !important;
		padding-left: 15px !important; padding-right: 15px !important;
	}
	/* 561-900 was landing at exactly 40px: Part 3 pins the height and the
	   phone rule that raises it stops at 560. The floor belongs on the whole
	   touch band, not just the phone. */
	body.pubv2 .callout .btn { min-height: 44px; }
}
@media only screen and (max-width: 560px) {
	body.pubv2 .callout .btn { width: 100%; min-height: 44px; margin-top: 14px; }
	body.pubv2 .callout h2,
	body.pubv2 .callout h3,
	body.pubv2 .callout h4 { font-size: 18px; }
}

/* ---------- section.alternate — the banding surface ----------
   layout-dark.css paints it #373737, which is lighter than every v2
   surface and reads as a grey stripe against --pv2-panel cards.

   --pv2-raised was the first fix and it collided: `.callout.dark` is
   --pv2-raised too, and on a standard game page the two are ADJACENT
   (locations band immediately followed by the "What is <game>?" band), so
   they rendered as one continuous field with no boundary. This is the
   middle rung of the ladder in 3.0 — one step above the page, one below
   the emphasis band. Declared here rather than in 3.0 because this is the
   rule that owns the class; 3.0 would have had to out-specify it. */
body.pubv2 section.alternate { background-color: var(--pv2-panel); }

/* ---------- THE RHYTHM GUARD — no two adjacent bands share a fill ----------
   Ryan, 2026-08-05: "it goes from one to the other, back and forth, back
   and forth. I want to make sure that's the case across all pages."

   The class→rung ladder above gets ~95% of the way there, but 64 distinct
   section orderings mean some templates put two same-rung bands flush
   against each other. Measured on the 323-page crawl: 10 alternate+alternate
   pairs, 11 alternate→wiki pairs, 1 wiki→alternate, 2 dark+dark callout
   pairs, 5 pairs of bare <section>s, 5 video-band→bare-<section> pairs,
   3 shader-section pairs. Every rule below targets the PATTERN of two
   same-rung bands adjacent — not a positional slot — so it is
   self-correcting for any ordering and inert on pages that already
   alternate. Part 5's "no positional selectors" ban forbids picking a
   SPECIFIC section by position because orderings differ; these rules are
   the opposite: whatever the ordering, the element they hit is by
   definition the one breaking the rhythm.

   Sibling reality, verified in the live DOM on every affected page
   (2026-08-05): the pairs named here are direct element siblings except
   the wiki band, which always has exactly one <style> between it and the
   band above — hence the `+ style +` variants. A `:has()` rule that stops
   matching (old browser, changed markup) fails SAFE: the band keeps its
   class rung and the page is exactly as it was before this guard.

   THE FLIPPED BAND KEEPS THE BANDED TREATMENT — weave + top hairline come
   from the class rules where the target already has them; the bare-section
   rules re-state them because a bare <section> has neither. */

/* two alternates flush: the second steps up to the emphasis rung
   (crosswind, windrose, subnautica-2, grounded2 — measured pairs) */
body.pubv2 section.alternate + section.alternate {
	background-color: var(--pv2-raised);
}
/* three in a row (grounded2): the third returns to the panel rung, so the
   run reads panel / raised / panel. (0,3,3) beats the pair rule's (0,2,2). */
body.pubv2 section.alternate + section.alternate + section.alternate {
	background-color: var(--pv2-panel);
}
/* ...unless the pair is followed by a dark callout (ark2, atlas,
   planet-explorers, rend): raised would merge with the callout, so the
   second alternate steps DOWN instead. Highest specificity here wins. */
body.pubv2 section.alternate + section.alternate:has(+ .callout.dark),
body.pubv2 section.alternate + section.alternate:has(+ style + .callout.dark) {
	background-color: var(--pv2-surface);
}
/* the wiki band directly after the locations band, both panel (11 pages:
   ASE, ASA, palworld, rust, terraria, hytale, humanitz, dune-awakening,
   runescape-dragonwilds, grounded2, unturned). The wiki plugin always
   emits one <style> first, so the direct form is future-proofing. */
body.pubv2 section.alternate + section.ss-wg,
body.pubv2 section.alternate + style + section.ss-wg {
	background-color: var(--pv2-raised);
}
/* and the reverse order (the-forest): the alternate after the wiki band */
body.pubv2 section.ss-wg + section.alternate {
	background-color: var(--pv2-raised);
}
/* two dark callouts flush (affiliates "Program Benefits" + "Content
   Creator", minecraft-legends' TrustBox strip + features): the FIRST one
   drops to panel — flipping the second would collide with the alternate
   band that follows on /affiliates/. Only :has can address the first of a
   pair; if unsupported the pair simply stays as it was. */
body.pubv2 .callout.dark:has(+ .callout.dark),
body.pubv2 .callout.dark:has(+ style + .callout.dark) {
	background-color: var(--pv2-panel);
}
/* two bare <section>s flush (cs2, csgo2, unturned, ASA, conan-exiles-
   enhanced — all five measured pairs precede an alternate band, so the
   second takes raised and the run reads surface / raised / panel) */
body.pubv2 section:not([class]) + section:not([class]) {
	background-color: var(--pv2-raised);
	background-image: var(--pv2p-weave);
	box-shadow: inset 0 1px 0 var(--pv2-line);
}
/* ...unless a dark callout follows (sbox): raised would merge with it, so
   that page's pair takes panel instead — same lookahead shape as the
   alternate pair above, same fail-safe. */
body.pubv2 section:not([class]) + section:not([class]):has(+ .callout.dark),
body.pubv2 section:not([class]) + section:not([class]):has(+ style + .callout.dark) {
	background-color: var(--pv2-panel);
}
/* the video band then a bare <section>, both on the page fill (dayz,
   derelicts, schedule-i, skyrim-together, arma2/overpoch) */
body.pubv2 section.nopadding + section:not([class]) {
	background-color: var(--pv2-panel);
	background-image: var(--pv2p-weave);
	box-shadow: inset 0 1px 0 var(--pv2-line);
}
/* the minecraft shader pages' bespoke band then a bare <section>
   (forge, meteor, optifine — the section that follows precedes an
   alternate, same shape as the bare-pair rule above) */
body.pubv2 section.shader-section + section:not([class]) {
	background-color: var(--pv2-raised);
	background-image: var(--pv2p-weave);
	box-shadow: inset 0 1px 0 var(--pv2-line);
}

/* ---------- .pv2p-tone-panel — the named band-tone utility ----------
   For a converted page whose markup order leaves two page-fill bands
   flush and where no generic pattern above applies (first use:
   /hytale-creators/, whose stat strip and "How to Become a Partner" are
   both bare .pv2p sections on the page fill). Inert for classic visitors
   — the class exists only under body.pubv2, same as every pv2p-* class
   that page already carries. */
body.pubv2 .pv2p-tone-panel {
	background-color: var(--pv2-panel);
	background-image: var(--pv2p-weave);
	box-shadow: inset 0 1px 0 var(--pv2-line);
}

/* ---------- ul.list-icon — REPAIR, not restyle ----------
   These bullets are BROKEN on the live public site and have been since
   the Font Awesome subset landed. essentials.css asks for
   `font-family: FontAwesome` at weight 400 — an FA4 family name the
   public pages do not load at all (they ship "Font Awesome 6 Free" at
   900 only). Verified on production: the ::before resolves to
   `content: ""` in family FontAwesome, i.e. an 18px empty box holding
   the indent open. Every check-list on ~296 templates is a list of
   invisible bullets today.

   Draw the mark in CSS instead so it can never depend on a font again.
   The box the theme reserves (18px wide, -25px pull) is kept exactly, so
   nothing reflows: only the mark inside it changes. */
body.pubv2 ul.list-icon > li:before {
	font-family: var(--pv2p-font);     /* stop asking for a font that is not here */
	content: '';
	position: relative;
	color: var(--pv2-accent);
	background: transparent;
	border: 0;
}

/* ---- THE SPECIFICITY TRAP THAT COST THIS COMPONENT TWO ATTEMPTS ----
   The theme puts the family modifier on the <ul> (`ul.list-icon.spaced
   .check-circle`), never on the <li>. The first version of this repair
   drew the tick with `ul.list-icon.check-circle > li:not(.check):before`
   — (0,4,4) — and the catch-all dot with
   `ul.list-icon > li:not(.check):not(.check-circle):not(.check-square)
   :before` — (0,5,4). The catch-all is MORE specific, matches the same
   <li> (the li carries no modifier), and won every property it declared.
   Measured on a live check-list: 5x5, border-radius 50%, background #666,
   plus the tick rule's stray `border-width: 0 2px 2px 0` and rotate(45deg)
   — a squashed grey circle wearing half a tick, on ~296 templates.

   The fix is not "raise the tick's specificity" — that is another race to
   lose. The three marks below are MUTUALLY EXCLUSIVE: the catch-all's
   :not() list names the family on the UL as well as the LI, so only one
   rule can ever match a given bullet and specificity is irrelevant. Add a
   new family here and you must add it to the catch-all's :not() list too. */

/* the tick — borders, rotated. Rides in the 18px box the theme reserves. */
body.pubv2 ul.list-icon.check > li:before,
body.pubv2 ul.list-icon.check-circle > li:before,
body.pubv2 ul.list-icon.check-square > li:before,
body.pubv2 ul.list-icon > li.check:before,
body.pubv2 ul.list-icon > li.check-circle:before,
body.pubv2 ul.list-icon > li.check-square:before {
	box-sizing: border-box;
	width: 6px; height: 11px;
	margin: 5px 6px 0 -22px;
	border: solid var(--pv2-accent); border-width: 0 2px 2px 0;
	border-radius: 0; background: transparent; transform: rotate(45deg);
}
/* the chevron — ul.list-icon.angle-right, the location lists on 293 game
   pages and /locations/. Same geometry as the tick, rotated the other way. */
body.pubv2 ul.list-icon.angle-right > li:before,
body.pubv2 ul.list-icon > li.angle-right:before {
	box-sizing: border-box;
	width: 7px; height: 7px;
	margin: 7px 8px 0 -21px;
	border: solid var(--pv2-accent); border-width: 2px 2px 0 0;
	border-radius: 0; background: transparent; transform: rotate(45deg);
}
/* everything else in the family gets a quiet dot rather than a hole */
body.pubv2 ul.list-icon:not(.check):not(.check-circle):not(.check-square):not(.angle-right) > li:not(.check):not(.check-circle):not(.check-square):not(.angle-right):before {
	width: 5px; height: 5px; margin: 9px 0 0 -21px;
	border: 0; border-radius: 50%; background: var(--pv2-text-dim);
	transform: none;
}
body.pubv2 ul.list-icon > li { line-height: 1.6; }
body.pubv2 ul.list-icon.spaced > li { margin-bottom: 7px; }

/* ============================================================
   ============================================================
   PART 4 — SITEWIDE MOBILE TAP TARGETS (shared header chrome)
   ============================================================

   From the sitewide mobile baseline (2026-08-04): 88 of 144 page/width
   combinations fail, ZERO pass, and 2,439 of 2,785 visible interactive
   elements measure under 44px at 390.

   That is not 300 pages' worth of problem. It is two selectors.

   These live in the shared header/footer partials, which this agent does
   not own — so they are fixed HERE, scoped body.pubv2, which lands them
   inside the staff preview without touching what customers see today.
   That gating is deliberate: the site stays whitelisted to staff until
   Ryan has reviewed it.

   Scoped to body.pubv2 + a width query, never a bare rule, so an
   unconverted page is still byte-identical on desktop.

   *** THE FOOTER HALF OF THIS SECTION MOVED OUT, 2026-08-04. ***
   The three footer rules — the 90-link list (81% of the whole site's
   tap-target problem, from one line), the social hit extenders and the
   Stripe/PayPal marks — are now css/public-v2-footer.css §11, because
   this file is linked ONLY on public pages and the same footer renders
   inside both panels. Keeping them here would have left the panel footer
   half-converted: the new skin with the old 31px tap rows.
   ============================================================ */

@media (hover: none) and (pointer: coarse), (max-width: 900px) {

	/* ---------- the hamburger ----------
	   header-default.css gives it `padding: 6px 10px` around a 21px glyph,
	   which measures 40x35 — the ONLY navigation control on a phone, under
	   the minimum on every page of the site. It is `hidden` on a page that
	   is already showing the v2 rail, so this is inert there and repairs
	   the classic header a pubv2 page falls back to (?hdr=off, or any
	   route the rail does not cover). Size, not a pseudo: it owns its
	   corner of the row and nothing shares its line.

	   Stays here: #topNav is the PUBLIC marketing header. Both panels
	   suppress it entirely, so this rule has nothing to reach there. */
	body.pubv2 #topNav button.btn-mobile {
		display: inline-flex; align-items: center; justify-content: center;
		min-width: 44px; min-height: 44px; padding: 0;
	}
	body.pubv2 #topNav button.btn-mobile i { line-height: 1; }
}

/* ============================================================
   ============================================================
   PART 5 — THE GAME-PAGE VOCABULARY   ** HIGH BLAST RADIUS **
   ============================================================

   This section finishes the 295 `services-game-*.tpl` templates without
   any of them being opened. It is written against the structural audit
   (goals/2026-08-04-public-v2-redesign/audit-game-pages.md), whose one
   decisive number is: 93.9% of every class token in every game-page body
   comes from a class that is on 200+ of the 295 files, and 211 of the 295
   files contain zero page-specific markup, CSS or JS. Those 211 are
   restyled entirely from here.

   IT INHERITS PART 3's CONTRACT AND ITS DANGER. Every selector below is a
   theme class, not a .pv2p-* class we own, because a page nobody edits has
   no class to opt in with. So the same rules apply:

     - conservative colour / type / spacing only,
     - no `display` change except the two documented mobile ones,
     - no `transform`, no `filter`, no `contain`, no new stacking context
       on anything that could sit above a payment form,
     - re-measure /affiliates/ (raw hero + raw accordion + both callouts)
       and /locations/<city>/ (world map + location list) after ANY edit —
       those two allowlisted pages share this vocabulary and are the only
       live proof available until the game routes join the allowlist.

   NEVER USE A POSITIONAL SELECTOR HERE. There are 64 distinct section
   orderings across the 295 files and the two largest clusters are the same
   13 sections in a different order, so :nth-of-type / + / :first-of-type
   silently hit the wrong section on a third of the site. The <section>
   element itself carries only four class values in the whole corpus —
   "" (629), alternate (348), nopadding (259), page-title (78) — so bare
   `section` is not a usable hook either. Target classes.

   File counts below are out of the 295 game templates.
   ============================================================ */

/* ------------------------------------------------------------
   5.0  THE PAGE RAIL — the first thing Ryan flagged on the preview, and
        the most visible thing on all 295.

   Measured on /services/game_servers/palworld/ at 1920:
       #c2brand .container   1560px   (the header, widened by PART 1)
       section  .container   1170px   (the body, still bootstrap's default)
   Sixteen band containers on that page, every one at 1170, sitting under a
   1560 rail. His words: "no new width increase". The pricing cards in
   particular sat in a 1170 box with 195px of dead space either side.

   WHICH RAIL. `--pv2p-rail-wide` — the same min(1560px, 100vw - 180px) the
   header brand row uses and about-v2.css was approved on. A game page is a
   marketing page with a pricing widget and a screenshot carousel: it is the
   .pv2p-w-wide case, not the .pv2p-w-read case a question list or a policy
   document gets. Because it is literally the same token the header uses,
   the two can never drift again — at 1440 both resolve 1260, at 1920 both
   resolve 1560, and below 1400 both fall back to the theme container, which
   is why 390 / 768 / 1100 do not move at all.

   HOW IT IS SCOPED. WAS `body.pubv2:not(:has(.pv2p))` — A PAGE-LEVEL TEST
   ON THE THEORY THAT "A PAGE THAT HAS OPTED IN OWNS ITS RAIL". CHANGED
   2026-08-04, AND HERE IS EXACTLY WHY.

   That gate was defensible when only the game pages were wide: a blanket
   widening would have stepped /faq/, /jobs/ and both /locations/ pages from
   1170 to 1560 while their content stayed on a 980 reading rail, and
   /home/ had chosen 1170 for every band it owns. Widening a page's chrome
   without widening its content is worse than leaving it alone.

   What it produced instead was the opposite failure, and Ryan named it:
   "it seems like a lot of the pages are still not using the full width."
   EVERY page that opted in WITHOUT naming a width fell back to the theme
   default — so the pages that got the most design attention rendered the
   narrowest, and /home/ (the flagship) was the worst of them at 1170 under
   a 1560 header.

   The fix is the PART 2 block that gives every opted-in page the wide rail
   as its default, which makes the gate here pointless: with the content on
   the rail, the chrome around it has to be too. So this is now a FLOOR for
   every pubv2 page, written with `:where(#wrapper)` so it carries less
   specificity than any component rule and a section that names its own
   rail still wins.

   It still costs nothing when a flagship agent converts a game page: the
   `.pv2p` rail resolves the same token, so the number does not move.

   IT WIDENS EVERY .container IN THE PAGE REGION, not an enumerated list of
   bands. An enumerated list would have missed the archetype-D notify
   callout (a class-less <div> wrapper, 20 files), `.compat-bar` (46), the
   shared `.callout.styleBackgroundColor` CTA that /faq/, /jobs/ and both
   /locations/ pages end with, /home/'s two dark callouts, and every
   page-unique flagship section — and each of those would have been a band
   stepping in relative to its neighbours. /home/ measured exactly that in
   the first pass of this change: bands at 1560 with the callout still at
   1170 between them.

   THE HOME HERO IS NOT A .container AND IS FIXED SEPARATELY — see the
   `.css-hero-inner` block in PART 2. It was the case that proved the point:
   `mobile-responsive.css` pins it to a flat 1170 and no rule here can reach
   it, so the caption sat 180px inboard of the bands under it.
   ------------------------------------------------------------ */
@media only screen and (min-width: 1400px) {
	/* every band in the page region, on EVERY pubv2 page.

	   `:where(#wrapper)` contributes ZERO specificity, so this selector lands
	   at (0,2,1) — lighter than `body.pubv2 .pv2p > .container` (0,3,1) and
	   lighter than any component rule. It is a FLOOR, not an override: a
	   section that opted in still owns its own rail, exactly as before. The
	   id stays (inside :where) because #wrapper is what confines this to the
	   page region — without it the selector would also reach a .container in
	   a modal or an injected third-party block. */
	body.pubv2 :where(#wrapper) .container {
		width: 100%;
		max-width: var(--pv2p-rail-wide);
		margin-left: auto; margin-right: auto;
	}
	/* ...except the classic #header container, which the v2 chrome replaces
	   with #c2brand and which must not become page-specific. #wrapper-prefixed
	   so it out-specifies the rule above rather than ties with it.

	   #footer USED TO BE PINNED HERE TOO, on the reasoning that a footer at
	   1560 on a game page and 1170 everywhere else is a worse mismatch than
	   the one being fixed. That reasoning expired when every pubv2 page moved
	   to the wide rail: the footer is now 1560 on ALL of them, so it is not
	   page-specific, and holding it at 1170 was the LAST disagreeing edge on
	   the site — measured at 1920, header 180, body 180, footer 375. The
	   footer rule now lives with the shared chrome below. */
	body.pubv2 #wrapper #header .container {
		width: 100%; max-width: 1170px;
	}
	/* ...and the brand row INSIDE it, which PART 1 widened on purpose and
	   which is the number this whole section exists to match. One id heavier
	   than the pin above, which it has to be: #c2brand is a child of #header,
	   so the pin matches its container too. */
	body.pubv2 #wrapper #header #c2brand .container {
		width: 100%; max-width: var(--pv2p-rail-wide);
	}
	/* A .container nested inside a .container pays the 15px gutter twice and
	   would stay boxed at the inner default while its parent widened — on a
	   standard game page that is `.container > .container.margin-top60`,
	   which holds the control-panel carousel, and /home/ ships the same
	   block inside a .pv2p section. This is what .pv2p-nest does for a page
	   that can add a class. */
	body.pubv2 #wrapper .container .container {
		width: auto; max-width: none;
		padding-left: 0; padding-right: 0;
		margin-left: 0; margin-right: 0;
	}
}

/* ------------------------------------------------------------
   5.1  THE HERO BAND — div.parallax.parallax-1 + span.parallax-overlay
        217 files, plus /affiliates/.

   The video band 60 lines down uses the SAME two classes and is told apart
   by `.text-center`, which is exact: 217 files carry `class="parallax
   parallax-1"` and 259 carry `class="parallax parallax-1 text-center"`,
   and there are exactly 217 `.parallax-overlay` spans. No third variant
   exists in the corpus. Verified by grep across all 295 templates.

   Three measured faults, all on every one of the 217:

   1. THE ART IS UNDER TWO DARKENING LAYERS AND THE TITLE IS UNDER BOTH.
      layout.css gives .parallax `background-color: rgba(0,0,0,0.8)` and
      the overlay `rgba(0,0,0,0.6)` PLUS a repeating dot-screen PNG. The
      overlay is `position:absolute; z-index:1`; .parallax-content carries
      `z-index:2` but is `position:static`, and z-index on a static element
      does nothing — so the film paints OVER the title, not under it. The
      per-game key art is the only thing on the page that says which game
      you are looking at and it renders as a grey texture.

      Fixed by giving .parallax-content `position: relative` so its
      existing z-index:2 finally applies. `position: relative` is the safe
      half of that pair: it does NOT create a containing block for
      `position: fixed` descendants the way `transform` does, so it cannot
      repeat the parallax-transform bug that trapped the Stripe modal
      below the footer. Nothing on a service-page hero is a payment form.

   2. THE CROP. layout.css @<=1024 forces `background-size: 1024px 100%
      !important`, which shows the middle 38% of the art stretched to the
      band height on every phone. Restored to `cover` (needs !important to
      beat it) with the focal point a little above centre, where game key
      art puts its subject.

   3. The title is 42px/300 with a 1px black text-shadow — the theme's
      2013 display face. On the v2 ramp it is a 32px/600 band title.
   ------------------------------------------------------------ */
body.pubv2 .parallax.parallax-1:not(.text-center) {
	padding: 52px 0 50px;
	background-color: var(--pv2-surface);
}
body.pubv2 .parallax.parallax-1:not(.text-center) > .parallax-overlay {
	/* one scrim, left-anchored: the title sits in a .col-md-6 on the left,
	   so the right half of the art can come almost all the way up. */
	background: linear-gradient(90deg,
		rgba(0, 0, 0, 0.88) 0%,
		rgba(0, 0, 0, 0.72) 34%,
		rgba(0, 0, 0, 0.38) 68%,
		rgba(0, 0, 0, 0.22) 100%);
}
body.pubv2 .parallax.parallax-1:not(.text-center) > .parallax-content { position: relative; }
/* THE HERO TITLE IS NOT THE LOUDEST THING ON THE PAGE.
   It shipped at 32px/600 pure white and Ryan's note was exact: "the new
   bold white text is bigger than the survival servers logo" — the wordmark
   beside it measures 34px tall, so a 32/600 title next to it competes with
   the brand instead of sitting under it. It is now the SAME band type every
   other v2 page uses (--pv2p-fs-band, 26px, weight 400), which is also the
   panel's own page-heading weight: CLAUDE-DESIGN-SYSTEM's "Page headings:
   24px, weight 400, #fff". One token, so a game page's title and a text
   page's title can never be two different sizes. */
body.pubv2 .parallax.parallax-1:not(.text-center) h1,
body.pubv2 .parallax.parallax-1:not(.text-center) h2 {
	font-family: var(--pv2p-font);
	font-size: var(--pv2p-fs-band); font-weight: 400; line-height: 1.25;
	color: #fff; letter-spacing: .01em;
	margin: 0; padding: 0; border: 0;
	text-shadow: none;
}
body.pubv2 .parallax.parallax-1:not(.text-center) .parallax-content p {
	margin: 9px 0 0; font-size: 14.5px; line-height: 1.55;
	color: var(--pv2-text-soft); text-shadow: none;
}
/* beat layout.css's `background-size: 1024px 100% !important` */
@media only screen and (max-width: 1024px) {
	body.pubv2 .parallax.parallax-1 {
		background-size: cover !important;
		background-position: 50% 42% !important;
	}
}
@media only screen and (max-width: 767px) {
	/* the title goes full width on a phone, so the scrim has to as well */
	body.pubv2 .parallax.parallax-1:not(.text-center) > .parallax-overlay {
		background: linear-gradient(180deg,
			rgba(0, 0, 0, 0.40) 0%,
			rgba(0, 0, 0, 0.62) 46%,
			rgba(0, 0, 0, 0.82) 100%);
	}
	body.pubv2 .parallax.parallax-1:not(.text-center) { padding: 34px 0 32px; }
}

/* ------------------------------------------------------------
   5.2  THE PLAIN-TITLE HERO — section.page-title, 78 files (archetype B:
        every ARK map mod, the ARMA mods, the older Minecraft modpacks).

   These pages open with an <h2> in a `section.page-title` and have no
   <h1> anywhere. That is an SEO defect and it is OUT OF SCOPE: changing a
   heading level is a content change. It is styled as the band it is.

   :not(.pv2p-band) keeps the four converted text pages and /locations/ on
   the Part 2 component — they carry both classes and would otherwise get
   two competing band treatments.
   ------------------------------------------------------------ */
body.pubv2 section.page-title:not(.pv2p-band) {
	padding: 26px 0 24px;
	background-color: var(--pv2-surface);
	background-image:
		radial-gradient(560px 220px at 100% 0%, rgba(234, 168, 36, 0.10), rgba(234, 168, 36, 0) 72%),
		repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.014) 0 1px, rgba(255, 255, 255, 0) 1px 8px);
	border-bottom: 1px solid var(--pv2-line);
}
body.pubv2 section.page-title:not(.pv2p-band) header { margin: 0; }
body.pubv2 section.page-title:not(.pv2p-band) h1,
body.pubv2 section.page-title:not(.pv2p-band) h2 {
	font-family: var(--pv2p-font);
	font-size: var(--pv2p-fs-band); font-weight: 400; line-height: 1.25;
	color: #fff; letter-spacing: .01em;
	/* layout.css spends `padding-bottom:15px` on a hairline that is
	   invisible on a dark background, and makes the heading inline-block */
	margin: 0; padding-bottom: 0; border-bottom: 0; display: block;
}
@media only screen and (max-width: 560px) {
	body.pubv2 section.page-title:not(.pv2p-band) { padding: 20px 0 18px; }
}

/* ------------------------------------------------------------
   5.3  THE FEATURE LIST — THE BIGGEST MOBILE WIN AVAILABLE ON THE SITE.
        288 of 295 files.

        <div class="row text-left hidden-xs">   <- the 12-24 selling bullets

   Bootstrap 3.3.2 ships `@media (max-width:767px){ .hidden-xs {
   display:none !important } }`. So on 288 of 295 pages a phone visitor
   reads "Our X server hosting plans come with the features you need" and
   then gets NOTHING — no instant setup, no FTP, no DDoS protection, none
   of the value proposition. Exactly one page in 295 ships a mobile
   replacement. Measured on four live pages at 390: 18, 22, 24 and 24
   bullets, all display:none.

   Revealing them is a PRESENTATION change and is inside the content
   freeze: the text is already in the DOM, already rendered to crawlers
   (the rule is a media query, not a server-side omission) and already
   indexed. No text is added, no markup changes, no heading moves.

   It needs !important to beat Bootstrap's own !important — that is
   planned for, not a smell. Scoped to `.callout .row.text-left` so it
   reveals the feature grid ONLY: the `.divider.half-margins.hidden-xs`
   rules above and below it are empty decorative divs and stay hidden,
   and no other .hidden-xs anywhere on the site is touched.
   ------------------------------------------------------------ */
@media only screen and (max-width: 767px) {
	body.pubv2 .callout .row.text-left.hidden-xs { display: block !important; }
	/* the three .col-md-4 columns are already full-width blocks below 768
	   (bootstrap leaves col-md-* unstyled there) — they just need air */
	body.pubv2 .callout .row.text-left.hidden-xs ul.list-icon { margin-bottom: var(--pv2p-s3); }
	body.pubv2 .callout .row.text-left.hidden-xs ul.list-icon:last-child { margin-bottom: 0; }
	body.pubv2 .callout .row.text-left.hidden-xs li { font-size: 13.5px; }
}
/* the bullets themselves, at every width. layout-dark paints them pure
   white, which puts them at the same weight as the heading above. */
body.pubv2 .callout ul.list-icon.check-circle > li,
body.pubv2 .callout ul.list-icon.check > li { color: var(--pv2-text-soft); }

/* ---- DENSITY — THE PLAIN-LIST ARCHETYPE ONLY.

   Ryan on /services/game_servers/7_days_to_die/: "it takes up way too
   much damn space."

   TWO ARCHETYPES SHIP THIS BAND AND ONLY ONE OF THEM IS LOOSE.
   The flagship pages (Palworld and friends) replace the bullet grid with
   their own `.pw-feat` block in the template's <style> — a 4-up grid of
   bordered heads with 4 tight bullets each, ~200px tall, which Ryan has
   approved and which NOTHING here touches: this selector is
   `.callout .row.text-left.hidden-xs`, and a page with `.pw-feat` has no
   such row. The plain archetype is 240 files with three
   `col-md-4 col-sm-4` columns of 8-9 bullets, plus 44 with a single
   column.

   MEASURED AT 1920 ON 7DTD BEFORE THIS BLOCK:
     li           30px box (padding 4px 0, line-height 22.4) + 7px margin
                  = a 37px pitch for a 22px line
     9 rows       333px
     .divider     26px margin each side, x2 dividers = 106px
     band         1059px total

   AFTER: 26px box + 3px margin = a 29px pitch (-8px x 9 rows = -72px) and
   16px divider margins (-40px). ~112px off the band with nothing removed
   and no bullet re-wrapped.

   WHAT IS DELIBERATELY NOT DONE: the row is NOT capped narrower to close
   the horizontal gaps. The longest bullet in the corpus is 77 characters
   ("Compatible with Xbox Game Pass/Windows/PlayStation 5 (full crossplay
   support)") and already needs most of a 465px column; pulling the
   columns in wraps bullets on dozens of pages to save whitespace, which
   is a worse trade. Vertical rhythm was the complaint and vertical rhythm
   is what moves.
   ---- */
body.pubv2 .callout .row.text-left.hidden-xs ul.list-icon > li {
	padding-top: 2px; padding-bottom: 2px;
	margin-bottom: 3px;
}
body.pubv2 .callout .row.text-left.hidden-xs ul.list-icon > li:last-child { margin-bottom: 0; }
/* the two hairlines that bracket the grid. 26px a side reads as a gap
   between three separate blocks; 16px reads as one band with a rule in
   it. Scoped to .callout so the section dividers elsewhere are untouched. */
body.pubv2 .callout .divider { margin-top: 16px; margin-bottom: 16px; }
/* the single-column variant, 44 files: one col-md-4 in a 1530 row is a
   list of bullets in the left third with 1020px of empty band beside it.
   Let it use the width it is already sitting in, and flow it into the
   same three columns the 240-file variant has. `columns` is safe on this
   list because ul.list-icon's marker is an absolutely positioned pseudo
   inside each li, so it travels with its own row. */
body.pubv2 .callout .row.text-left.hidden-xs > [class*="col-"]:only-child {
	float: none; width: 100%;
}
@media only screen and (min-width: 992px) {
	body.pubv2 .callout .row.text-left.hidden-xs > [class*="col-"]:only-child > ul.list-icon {
		column-count: 3; column-gap: 40px;
	}
	body.pubv2 .callout .row.text-left.hidden-xs > [class*="col-"]:only-child > ul.list-icon > li {
		-webkit-column-break-inside: avoid; break-inside: avoid;
	}
}

/* ------------------------------------------------------------
   5.3b  THE FOUR-COLUMN FEATURE BAND — ONE COMPONENT, `.pw-feat`.

   Ryan, 2026-08-05, comparing /palworld/ with /7_days_to_die/: "compare the
   pages — basically everything is reworked on 7 Days. I don't like that," and
   "look how off the margins are."

   HE WAS LOOKING AT DRIFT BETWEEN TWO CODEBASES. The first cut of this band
   shipped a NEW component (`.pv2p-feat4`) built to RESEMBLE the hand-built
   grid Palworld, ASA and Valheim had carried since before the redesign. Two
   implementations of one design diverge by definition; measured at 1600 they
   already had, on every value the eye reads:

                         Palworld (.pw-feat)   7 Days (.pv2p-feat4)
     band padding-top     30px                  8px
     grid margin          14px 0 26px           6px 0 4px
     column gap           24px                  10px
     column box           none                  panel + border + 12/10/13 pad
     column width         329.5px               340px
     lead plate height    47.1px                52.4px
     first tick from top  51.1px                81.4px
     grid height          183.1px               228.7px

   So `.pv2p-feat4` is DELETED and this section is Palworld's own grid, lifted
   verbatim out of that page's template <style> and promoted to the sheet. The
   342 generated pages now emit Palworld's markup — `.pw-feat` > `ul` >
   `li.pw-star.pw-<accent>` first, plain `li` after — and take these rules, so
   the pages are identical BY CONSTRUCTION rather than by resemblance. The
   three flagship templates keep their own copy for the classic render and
   gate it off under v2, so in v2 there is exactly one definition on the site.

   THE ONE THING PALWORLD'S GRID COULD NOT CARRY, and what was done about it:
   `white-space: nowrap` on every line. Palworld's, ASA's and Valheim's lines
   are hand-written short; the generated pages carry the corpus's real copy,
   up to "Compatible with Xbox Game Pass/Windows/PlayStation 5 (full crossplay
   support)" at 77 characters. With nowrap a track sizes to its longest line,
   so the four tracks come out unequal and the grid overflows its container —
   which PALWORLD ALREADY DOES: measured 313/302/268/246 tracks at 1366 in a
   1170 container, and a 1216px document in a 1200px viewport, i.e. a real
   horizontal scrollbar. Dropping nowrap (plus `min-width: 0` on the tracks)
   makes the four tracks equal at every width, removes that scrollbar, and
   costs Palworld one wrapped tick line below 1440 (grid 183.1 -> 206.1).
   At 1600 — where the container is 1390 and the tracks are 329.5 — Palworld
   is unchanged to the pixel. Making the component accept content was the job;
   this is the value that had to give for it to.

   Everything else here is Palworld's number: the 19px gold circle tick at
   left 4 / top 6, the star plate's 7px/40px with its 28px tile, grid margin
   14/26, the 1199 step to two tracks. The selector groups carry `.asa-feat`
   and `.vh-feat` because ASA and Valheim ship the same grid under their own
   prefix — one rule set, three names, not three rule sets.

   THE HORIZONTAL BUDGET — WHY THE GAP IS 16 AND THE LI PADDING IS 27/0.

   Dropping nowrap made the tracks equal, and then the corpus's real copy
   started wrapping in them: measured across all 353 templates at 1440,
   578 lines on 287 of the 293 English pages. Ryan on /7_days_to_die/ at
   1920: "make sure that there's no word wrap ... this is a huge space
   killer" — every wrapped line adds a row of dead height to the whole band.

   Re-adding `white-space: nowrap` is the one fix that is NOT available: it
   is what the block above deleted, and it brings back unequal tracks and a
   1216px document in a 1200px viewport. So the wrap is closed from the two
   ends that are actually free — layout width first, then copy.

   WHAT WAS RECLAIMED. One idea, applied everywhere it was free: the band
   was carrying dead horizontal padding, and the tick/icon channels were
   wider than they read. Per track, at every width:
     gap 24 -> 16           +6.0  three gutters shared over four tracks
     li padding-right 6->0  +6.0  dead space; the gap already separates the
                                  columns and a plain li has no background
     li padding-left 30->27 +3.5  the 19px tick at left 4 ends at x=23, so
                                  30 left a 7px channel; 27 makes it 4px
     star padding 6/40 -> 4/36     the 28px tile at left 4 ends at x=32, so
                                  40 left an 8px channel; 36 makes it 4px,
                                  the SAME channel the tick now has - the
                                  two agreeing is why 36 is the number
   Net: +15.5px of text width per plain line, +6px per star line.

   WHAT WAS NOT TOUCHED, AND WHY. The band is ALREADY on the full page rail
   — measured 105..1335 at 1440, the same box as the h2 above it and the
   pricing cards below it — so there is no container width to win back; the
   only way to widen it further is to break it out of the .container's 15px
   padding, which would step it 15px out of line with those two neighbours,
   and misaligned margins in this band are a thing Ryan has already
   rejected once. Font-size is untouched — shrinking the type to dodge a
   wrap degrades the component to fix a symptom.

   Ryan's four approved measurements are unchanged by all of the above, and
   they are the regression test for this block: band padding-top 30,
   heading-to-grid 42.5, plate height 48.4, first tick 58.4.
   ------------------------------------------------------------ */
body.pubv2 .pw-feat,
body.pubv2 .asa-feat,
body.pubv2 .vh-feat {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 0 16px;
	text-align: left;
	margin: 14px 0 26px;
}
/* bootstrap's clearfix pseudos would take real grid cells if this ever lands
   on a .row — the same guard §4b put on .pv2p-grid */
body.pubv2 .pw-feat:before, body.pubv2 .pw-feat:after,
body.pubv2 .asa-feat:before, body.pubv2 .asa-feat:after,
body.pubv2 .vh-feat:before, body.pubv2 .vh-feat:after { content: none; }
body.pubv2 .pw-feat ul,
body.pubv2 .asa-feat ul,
body.pubv2 .vh-feat ul {
	list-style: none;
	padding: 0;
	margin: 0;
	/* a grid item's automatic minimum is its min-content width; pin it to 0 so
	   a long line can never widen its own track past its 1fr share */
	min-width: 0;
}
body.pubv2 .pw-feat li,
body.pubv2 .asa-feat li,
body.pubv2 .vh-feat li {
	position: relative;
	padding: 5px 0 5px 27px;
	font-size: 13.5px;
	color: #c9cfd5;
}
body.pubv2 .pw-feat li b,
body.pubv2 .asa-feat li b,
body.pubv2 .vh-feat li b { color: #e9edf0; font-weight: 600; }
body.pubv2 .pw-feat li:not(.pw-star)::before,
body.pubv2 .asa-feat li:not(.asa-star)::before,
body.pubv2 .vh-feat li:not(.vh-star)::before {
	content: "\2713";
	position: absolute;
	left: 4px; top: 6px;
	width: 19px; height: 19px;
	margin: 0; border: 0;
	background: #d99a15;
	color: #17191d;
	border-radius: 50%;
	transform: none;
	font-size: 12px;
	font-weight: 800;
	text-align: center;
	line-height: 19px;
}
body.pubv2 .pw-star,
body.pubv2 .asa-star,
body.pubv2 .vh-star {
	padding: 7px 4px 7px 36px !important;
	margin-bottom: 4px;
	border-radius: 8px;
}
body.pubv2 .pw-star i,
body.pubv2 .asa-star i,
body.pubv2 .vh-star i {
	position: absolute;
	left: 4px; top: 7px;
	width: 28px; height: 28px;
	border-radius: 8px;
	font-style: normal;
	font-size: 15px;
	text-align: center;
	line-height: 28px;
}
body.pubv2 .pw-star > span,
body.pubv2 .asa-star > span,
body.pubv2 .vh-star > span {
	display: block;
	color: #fff;
	font-weight: 700;
	font-size: 14px;
	line-height: 1.25;
}
body.pubv2 .pw-star em,
body.pubv2 .asa-star em,
body.pubv2 .vh-star em {
	display: block;
	font-style: normal;
	font-weight: 500;
	font-size: 12.5px;
	line-height: 1.35;
	color: #d6dbe0;
}
body.pubv2 .pw-star em .pw-soon { color: #f2c522; font-weight: 600; }
body.pubv2 .pw-green  { background: rgba(18,39,26,.4);  box-shadow: inset 3px 0 0 #2ee06e; }
body.pubv2 .pw-blue   { background: rgba(16,31,49,.4);  box-shadow: inset 3px 0 0 #58a6ff; }
body.pubv2 .pw-gold   { background: rgba(42,33,12,.4);  box-shadow: inset 3px 0 0 #f2c522; }
body.pubv2 .pw-purple { background: rgba(29,19,46,.4);  box-shadow: inset 3px 0 0 #9b5cf6; }
body.pubv2 .pw-green i  { background: #133020; color: #2ee06e; }
body.pubv2 .pw-blue i   { background: #12233c; color: #58a6ff; }
body.pubv2 .pw-gold i   { background: #33290e; color: #f2c522; font-size: 18px; }
body.pubv2 .pw-purple i { background: #241738; color: #b98cf9; }
body.pubv2 .asa-green  { background: rgba(18,39,26,.4);  box-shadow: inset 3px 0 0 #2ee06e; }
body.pubv2 .asa-blue   { background: rgba(16,31,49,.4);  box-shadow: inset 3px 0 0 #58a6ff; }
body.pubv2 .asa-gold   { background: rgba(42,33,12,.4);  box-shadow: inset 3px 0 0 #f2c522; }
body.pubv2 .asa-purple { background: rgba(29,19,46,.4);  box-shadow: inset 3px 0 0 #9b5cf6; }
body.pubv2 .asa-green i  { background: #133020; color: #2ee06e; }
body.pubv2 .asa-blue i   { background: #12233c; color: #58a6ff; }
body.pubv2 .asa-gold i   { background: #33290e; color: #f2c522; font-size: 18px; }
body.pubv2 .asa-purple i { background: #241738; color: #b98cf9; }
body.pubv2 .vh-green  { background: rgba(18,39,26,.4);  box-shadow: inset 3px 0 0 #2ee06e; }
body.pubv2 .vh-blue   { background: rgba(16,31,49,.4);  box-shadow: inset 3px 0 0 #58a6ff; }
body.pubv2 .vh-gold   { background: rgba(42,33,12,.4);  box-shadow: inset 3px 0 0 #f2c522; }
body.pubv2 .vh-purple { background: rgba(29,19,46,.4);  box-shadow: inset 3px 0 0 #9b5cf6; }
body.pubv2 .vh-green i  { background: #133020; color: #2ee06e; }
body.pubv2 .vh-blue i   { background: #12233c; color: #58a6ff; }
body.pubv2 .vh-gold i   { background: #33290e; color: #f2c522; font-size: 18px; }
body.pubv2 .vh-purple i { background: #241738; color: #b98cf9; }
/* Palworld's own ramp: four tracks to 1200, then two. */
@media only screen and (max-width: 1199px) {
	body.pubv2 .pw-feat,
	body.pubv2 .asa-feat,
	body.pubv2 .vh-feat { grid-template-columns: repeat(2, 1fr); }
}
/* ---- THE GRID ON A PHONE.

   All 345 templates mark the grid `.hidden-xs`, and bootstrap ships
   `@media (max-width:767px){ .hidden-xs{ display:none !important } }` — so a
   phone visitor reads "Our X server hosting plans come with the features you
   need" and then gets NOTHING. Measured at 390 on Palworld before this:
   display:none, height 0, on the highest-revenue page on the site.

   Revealing is a PRESENTATION change and is inside the content freeze: the
   text is already in the DOM, already crawled (the rule is a media query, not
   a server-side omission) and already indexed. Scoped to body.pubv2, so the
   classic render still hides it and stays byte for byte what it was.
   ---- */
@media only screen and (max-width: 767px) {
	body.pubv2 .pw-feat,
	body.pubv2 .asa-feat,
	body.pubv2 .vh-feat {
		display: grid !important;
		grid-template-columns: 1fr;
		gap: 12px;
		margin: 14px 0 20px;
	}
}

/* ------------------------------------------------------------
   5.3c  THE BAND'S OWN RHYTHM — also Palworld's, also promoted.

   The three lines the flagship templates set around their grid, so the run
   features -> Trustpilot -> toggle -> pricing cards is one rhythm on all 345
   pages instead of two. `:has()` keeps them on the game service pages and off
   /locations/, /about/ and the text pages, which share `.parallax-1` but have
   no feature grid and no fold problem to fix.

   Ryan: "we squeeze that little top bar where the background Minecraft thing
   is so that the price cards can show. That needs to be consistent
   throughout." — 28px is the squeeze Palworld has shipped all along, so
   "consistent throughout" now means consistent WITH Palworld.

   The two `.divider.half-margins` hairlines and the xs-only `&nbsp;` spacer
   that the generated pages carry around the band are hidden rather than cut
   from 342 templates: they are empty decorative divs, Palworld has neither,
   and they are what put the generated heading-to-grid gap 53px below
   Palworld's. Nothing with text in it is hidden.
   ------------------------------------------------------------ */
body.pubv2:has(.pw-feat, .asa-feat, .vh-feat) .parallax.parallax-1 {
	padding-top: 28px;
	padding-bottom: 28px;
	min-height: 0;
}
body.pubv2:has(.pw-feat, .asa-feat, .vh-feat) .callout.dark .premium-toggle-container {
	margin-bottom: 0;
	padding: 10px 0 0;
}
body.pubv2:has(.pw-feat, .asa-feat, .vh-feat) .callout.dark .pricing-cards-container {
	margin-top: 10px;
}
body.pubv2:has(.pw-feat, .asa-feat, .vh-feat) .callout.dark .divider.half-margins,
body.pubv2:has(.pw-feat, .asa-feat, .vh-feat) .callout.dark > .container > p.hidden-lg.hidden-md.hidden-sm {
	display: none;
}

/* ------------------------------------------------------------
   5.3e  THE PLATFORM COMPATIBILITY BAR — `.pv2p-compat`

   Ryan: "it shows the platform's compatibility in that yellow bar below" —
   the strip under the Palworld pricing reading "Deploy an Instant Palworld
   Game Server - compatible with: Steam (PC) - Xbox (Game Pass) - PlayStation 5
   (full crossplay)". Every game needs its own accurate version.

   ACCURACY IS THE WHOLE COMPONENT. The bar is only emitted where the platform
   can be PROVEN from our own data - a real Steam AppID in
   sspanel/includes/game-meta.inc.php, which that file resolved against the
   Steam storefront API. A page whose game has no AppID gets NO bar rather than
   a guessed one, and no chip is ever rendered for a console or for crossplay
   unless the page already asserts it, because `platforms => array('pc')` in
   that file means "no console evidence was collected", NOT "this game is PC
   only". The 42 templates that already ship a hand-written `.compat-bar` are
   left exactly as they are.

   The label says what OUR SERVER accepts, which is the same claim the existing
   hand-written bars make - not what the game ships on.
   ------------------------------------------------------------ */
body.pubv2 .pv2p-compat {
	padding: 14px 0;
	background: linear-gradient(90deg, #1a1a1a, rgba(255, 194, 51, 0.13), #1a1a1a,
		rgba(255, 194, 51, 0.13), #1a1a1a);
	background-size: 300% 100%;
	border-top: 2px solid var(--pv2-gold);
	border-bottom: 2px solid var(--pv2-gold);
	text-align: center;
	animation: pv2pCompatSweep 8s ease infinite;
}
@keyframes pv2pCompatSweep {
	0%   { background-position: 0% 50%; }
	50%  { background-position: 100% 50%; }
	100% { background-position: 0% 50%; }
}
/* an 8s infinite background animation runs on every game page, so it yields
   the moment the visitor has asked for less motion */
@media (prefers-reduced-motion: reduce) {
	body.pubv2 .pv2p-compat { animation: none; background-position: 50% 50%; }
}
body.pubv2 .pv2p-compat-label {
	color: var(--pv2-gold);
	font-family: var(--pv2p-font);
	font-size: 14px;
	font-weight: 600;
}
body.pubv2 .pv2p-compat-p {
	display: inline-block;
	margin-left: 20px;
	color: #fff;
	font-family: var(--pv2p-font);
	font-size: 14px;
	white-space: nowrap;
}
body.pubv2 .pv2p-compat-p > [class^="icon-"] {
	margin-right: 6px;
	color: var(--pv2-gold);
}
body.pubv2 .pv2p-compat-p .note {
	color: #8a9098;
	font-size: 12px;
}
@media only screen and (max-width: 767px) {
	body.pubv2 .pv2p-compat { padding: 12px 0; }
	body.pubv2 .pv2p-compat-label,
	body.pubv2 .pv2p-compat-p { display: block; font-size: 13px; }
	body.pubv2 .pv2p-compat-p { margin: 6px 0 0; }
}

/* ------------------------------------------------------------
   5.3d  THE MOD / MAP / PARTNER TILE GRID — `.box-content.thumbnail`

   Ryan: "across multiple games we have these sort of mod lists or we have
   partner lists. They take up way too much space and they look really old
   school. We need a refreshed version."

   75 live tiles across 12 templates. Minecraft alone carries 32 in one grid of
   eight four-up rows; ARK 14, ASA 10, ARMA3 7, and single tiles on six more.
   MEASURED before: a tile is a 320x275 image under a black caption bar, so one
   Minecraft row is ~360px tall and the grid runs ~2,900px — several screens of
   scrolling for 32 links.

   This is a DENSITY AND SURFACE change only. Every tile, every link, every
   caption and every "One click" label stays exactly as authored — the artwork
   is capped to a 16:9 band, the caption comes out of its black bar and onto the
   card, and the card picks up the same panel language as the rest of v2.

   TWO TRAPS, both real:
   1. `.box-content.thumbnail` is ALSO the order flow's game-picker card
      (order/index.php and sspanel/user/index.php), where it is a <td> and
      carries `.game-card-ribbon`. Restyling those would reach the order page,
      which is the one surface where getting it wrong costs revenue. Every rule
      below is guarded with `:not(.game-card-ribbon)`.
   2. 31 of the 32 Minecraft images carry an INLINE `width:320px;height:275px`.
      Inline beats a stylesheet, so the image sizing needs `!important` — the
      only place in this block that does.
   ------------------------------------------------------------ */
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) {
	position: relative;
	/* layout.css:1065 ships `display:inline-block; max-width:255px`, so the tile
	   is shrink-to-fit and a 100%-wide image inside it has nothing to resolve
	   against — it collapsed to 2px in the harness. Block + full width first. */
	display: block;
	width: 100%;
	max-width: none;
	padding: 0;
	margin-bottom: 14px;
	background: var(--pv2-panel);
	border: 1px solid var(--pv2-border);
	border-radius: 8px;
	overflow: hidden;
	transition: border-color .14s, transform .14s;
}
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon):hover {
	border-color: var(--pv2-gold);
	transform: translateY(-2px);
}
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) > a.item-image,
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) > .item-image {
	display: block;          /* inline-block in layout.css:1077 */
	width: 100%;
	text-decoration: none;
}
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) img {
	display: block;
	width: 100% !important;
	height: 104px !important;
	object-fit: cover;
	object-position: 50% 40%;
	margin: 0;
	border: 0;
	border-radius: 0;
	transition: opacity .14s;
}
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon):hover img { opacity: .82; }
/* the caption. It was a black bar under the art; it is now the card's own
   footer, so the tile loses ~120px without losing a character of copy. */
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) h3 {
	/* layout.css:1082 pins the caption `position:absolute; bottom:0; left:0`
	   ON TOP of the artwork. It becomes a normal footer row here. */
	position: static;
	margin: 0;
	padding: 8px 10px 9px;
	background: none;
	border: 0;
	font-family: var(--pv2p-font);
	font-size: 13px;
	line-height: 1.25;
	text-align: left;
	color: var(--pv2-text-soft);
}
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) h3 > br { display: none; }
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) h3 span {
	display: block;
	/* the black plate behind the name and the white plate behind "One click"
	   (layout.css:1093 and :1098) are what read as old-school. */
	background: none;
	padding: 0;
	color: #fff;
	font-size: 13.5px;
	font-weight: 600;
	letter-spacing: 0;
}
/* "One click" stops being a second line and becomes a quiet label under the
   name. Partner tiles have no <small> at all and simply render the name. */
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) h3 small {
	display: block;
	background: none;
	padding: 0;
	margin-top: 2px;
	color: #8a9098;
	font-size: 11px;
	font-weight: 400;
	text-transform: none;
}
body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) p {
	margin: 0;
	padding: 0 10px 9px;
	font-size: 12px;
	color: var(--pv2-text-soft);
	text-align: left;
}
/* Six tiles per row instead of four once the artwork is a 16:9 band — the
   markup fixes four columns per `.row`, so the gain is purely vertical:
   a Minecraft row goes ~360px -> ~150px. */
@media only screen and (max-width: 767px) {
	body.pubv2 .box-content.thumbnail:not(.game-card-ribbon) img {
		height: 92px !important;
	}
}

/* ------------------------------------------------------------
   5.4  SECTION CHROME — the heading ramp and the vertical rhythm on the
        two class-less sections every game page has (the control-panel
        section and the FAQ section), 293 and 294 files.

   `layout.css section header { margin-bottom: 60px }` and
   `.container.margin-top60 { margin-top: 60px }` stack, so the panel
   section ships 120px of dead space between its subtitle and the slider.
   Both come onto the one rhythm scale: 26 + 36 = 62px.

   The :not() chain is load-bearing. Every converted page puts its content
   in `section.pv2p` / `section.page-title.pv2p-band`, and both already
   have `header { margin: 0 }` from Part 2 at (0,2,1). This selector is
   (0,3,1) and would out-specify them and put a 26px margin back.
   ------------------------------------------------------------ */
/* RYAN, 2026-08-05: "the panel intro block is lopsided - heading and paragraph
   occupy the left half, the entire right half is empty." It was: the <h4> is
   held to a reading measure (96ch, ~710px) inside a 1420px rail, so the whole
   intro sat in the left half with 710px of nothing beside it.

   The measure is not the bug and is not being widened — the contract holds
   prose to an 80-86 character measure and a 1420px line is unreadable. What
   changes is where the measure sits: the header block is centred, so the same
   710px of empty rail becomes 355px of symmetric margin on each side instead of
   one dead half. It also puts this intro on the same centre line as the feature
   band directly above it, which is already centred.

   The vertical gap is tightened at the same time (26 to 18), the other half of
   the same note.

   SCOPED WITH `:has(> h4)` ON PURPOSE. 357 of the 927 headers on these pages
   are this pattern — a title with a paragraph of body copy under it. The other
   570 are a bare section title sitting above left-aligned content ("How do I
   host a <game> Server?" and its bullet list), and centring THOSE puts a
   centred heading over a left-aligned body, which is a different change and
   not the one that was asked for. Without `:has` the rule simply does not
   apply and the block falls back to exactly what it is today. */
body.pubv2 section:not(.page-title):not(.pv2p):not(.pv2p-band) > .container > header {
	margin: 0 0 var(--pv2p-s4);
}
body.pubv2 section:not(.page-title):not(.pv2p):not(.pv2p-band) > .container > header:has(> h4),
body.pubv2 section:not(.page-title):not(.pv2p):not(.pv2p-band) > .container > header:has(> h5) {
	margin-left: auto;
	margin-right: auto;
	max-width: 96ch;
	text-align: center;
}
/* WEIGHT 400, NOT 600. Ryan on the first Part 5 pass: "everything is bold
   and yucky" — every head on the page was 600 and #fff, so nothing had a
   volume relative to anything else. The hierarchy is now band title 26/400
   > section head 20/400 > sub-head 17/400 > the <h4> that is really body
   copy 14.5/400 MUTED, and the only 600 left in a band is the interactive
   chrome (a card head bar, an accordion row). Colour carries as much of it
   as size: #fff for the one loudest line, --pv2-text-muted for everything
   supporting it. The site's own {wiki_guides} section head — shipped,
   unflagged, on ~250 game pages — is 22px/400, which is the same call. */
body.pubv2 section:not(.page-title):not(.pv2p):not(.pv2p-band) > .container > header > h1,
body.pubv2 section:not(.page-title):not(.pv2p):not(.pv2p-band) > .container > header > h2,
body.pubv2 section.alternate > .container > h2,
body.pubv2 section.alternate > .container > h3 {
	font-family: var(--pv2p-font);
	font-size: 20px; font-weight: 400; line-height: 1.35;
	color: #fff; letter-spacing: .005em; text-transform: none;
	margin: 0 0 var(--pv2p-s2); padding: 0; border: 0;
}
body.pubv2 section:not(.page-title):not(.pv2p):not(.pv2p-band) > .container > header > h3 {
	font-family: var(--pv2p-font);
	font-size: 17px; font-weight: 400; line-height: 1.4; color: #fff;
	margin: 0 0 var(--pv2p-s2); padding: 0; border: 0;
}
/* the <h4> under those <h2>s is a paragraph of body copy wearing a
   heading tag — 293 files. The tag is frozen; the weight is not. */
body.pubv2 section:not(.page-title):not(.pv2p):not(.pv2p-band) > .container > header > h4,
body.pubv2 section:not(.page-title):not(.pv2p):not(.pv2p-band) > .container > header > h5 {
	font-family: var(--pv2p-font);
	font-size: 14.5px; font-weight: 400; line-height: 1.62;
	color: var(--pv2-text-muted); text-transform: none; letter-spacing: 0;
	/* the measure now lives on the centred header above; this keeps the
	   cap for any header that is not the class-less game-page one. */
	margin: 0 auto; padding: 0; border: 0; max-width: 96ch;
}
body.pubv2 section.alternate > .container > p {
	font-size: 14.5px; line-height: 1.6; color: var(--pv2-text-muted);
	margin: 0 0 var(--pv2p-s5);
}
/* the theme's 60px utilities, brought onto the one rhythm scale. Pure
   spacing, no layout property. 293 game pages + the home page. */
body.pubv2 .container.margin-top60 { margin-top: var(--pv2p-s6); }
body.pubv2 .margin-bottom60 { margin-bottom: var(--pv2p-s6); }
/* .divider is 4px of double hairline in #ddd — two rules 4px apart on a
   dark page read as a rendering mistake. One quiet line instead. */
body.pubv2 .divider {
	height: 1px; border-top: 1px solid var(--pv2-line); border-bottom: 0;
}
body.pubv2 .dark .divider,
body.pubv2 .callout.dark .divider { border-top-color: var(--pv2-border); }
body.pubv2 .divider.half-margins { margin: var(--pv2p-s5) 0; }

/* ------------------------------------------------------------
   5.5  THE LOCATIONS BAND — section.alternate, 293 files + /locations/.

   THE WORLD MAP, AND WHY IT IS HIDDEN ON PHONES RATHER THAN REDESIGNED.

   `<div class="row world-map m-b"></div>` is EMPTY MARKUP. It holds no
   text, no link, no image, no alt — jvectormap draws an SVG into it at
   runtime and the post-footer of 293 files pins it with
   `jQuery('.world-map').css('height','342px')`, an inline write no
   stylesheet can beat. Measured at 390 on eight live pages: the
   projection renders about twice the viewport wide inside a container
   that does not pan, and TWO OF THE EIGHT LOCATION MARKERS ARE
   UNREACHABLE. The section exists to advertise eight datacenters and on a
   phone it silently hides a quarter of them.

   Three options existed. (a) Make it responsive — impossible from CSS,
   the height is an inline JS write and the scale is computed by the
   plugin. (b) Scroll it inside a wrapper — the plugin owns pan/zoom and
   the container, and a 342px-tall horizontal scroller of a world map is
   not a thing anyone will use on a phone. (c) Hide the DECORATION and let
   the CONTENT carry the section.

   (c), and it is inside the content freeze precisely because the element
   is empty: below the map sit eight <a> links to /locations/<city>/ with
   their DDoS labels, and those are what the section actually says. They
   stay, and they finally get a readable row. Deleting the JS height lock
   across 293 files is still the right permanent fix (audit sweep #2) and
   is not this file's to make.
   ------------------------------------------------------------ */
body.pubv2 .world-map {
	border-radius: var(--pv2-radius-lg);
	background: var(--pv2-surface);
}
@media only screen and (max-width: 767px) {
	body.pubv2 .world-map { display: none !important; }
	/* the <br/> the templates put under the map, so the list does not
	   start on a stray blank line once the map is gone */
	body.pubv2 section.alternate > .container > br { display: none; }

	/* ...UNLESS the div now holds the shared inline SVG. Everything above
	   was written about the jvectormap plugin: the runtime height lock, the
	   projection twice the viewport wide, the two unreachable markers. The
	   /locations/<city>/ pages have since been rebuilt onto the same
	   responsive SVG the game pages use - it scales with its box and every
	   marker is inside the frame at 390 - so hiding it now costs the reader
	   the map for a reason that no longer exists. Game pages dodged this
	   because their wrapper is .pv2p-worldmap-wrap; the city pages kept the
	   legacy class, which is the only reason they were caught.
	   A browser without :has() simply keeps the old behaviour. */
	body.pubv2 .world-map:has(> svg.pv2p-worldmap) { display: block !important; }
}
/* the location list. The <a> carries an inline `style="color:white;"` on
   2,344 occurrences across 293 files, which beats any stylesheet — audit
   sweep #1 deletes it. Until then white is a fine colour for it and this
   only owns the geometry and the label beside it. */
body.pubv2 ul.list-icon.angle-right { margin-bottom: var(--pv2p-s2); }
body.pubv2 ul.list-icon.angle-right > li { margin-bottom: 4px; }
body.pubv2 ul.list-icon.angle-right > li > a {
	display: inline-flex; align-items: center;
	min-height: 24px; vertical-align: middle;
	font-size: 14px; font-weight: 400;
}
body.pubv2 ul.list-icon > li > .label.label-default {
	display: inline-block; vertical-align: middle;
	background: var(--pv2-field); border: 1px solid var(--pv2-border);
	border-radius: 11px; padding: 3px 9px;
	font-family: var(--pv2p-font);
	font-size: 10.5px; font-weight: 600; line-height: 1.2;
	letter-spacing: .03em; color: var(--pv2-text-muted);
	text-shadow: none;
}

/* ------------------------------------------------------------
   5.6  THE VIDEO BAND — section.nopadding > .parallax.parallax-1
        .text-center, 259 files.

   No background image and no overlay on this one: it inherits
   `background-color:#333` from essentials plus `rgba(0,0,0,0.8)` from
   layout, which lands it as a grey slab between two v2 surfaces. Colour
   only, plus a frame on the embed so the video stops being a raw
   rectangle bleeding into the band.
   ------------------------------------------------------------ */
body.pubv2 .parallax.parallax-1.text-center {
	background-color: var(--pv2-surface);
	padding: var(--pv2p-s6) 0;
}
body.pubv2 .parallax.parallax-1.text-center .embed-responsive {
	border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius-lg);
	overflow: hidden;
	background: #000;
	box-shadow: var(--pv2p-shadow);
}
body.pubv2 .parallax.parallax-1.text-center .embed-responsive-item { border: 0; }

/* ------------------------------------------------------------
   5.6b  CUSTOMER TESTIMONIALS — .testimonial.classic, on the game pages
         that carry the section (valheim, v-rising).

   Ryan, 2026-08-05: "these testimonial blurbs don't look anything like
   the one from the index page, and the overly white text doesn't match
   the fit, finish and feel." Measured on /services/game_servers/valheim/:
   the quote <p> renders #F6F6F6 with #000 text — the 2013 white card —
   plus a 52px left pad reserving space for a male/female avatar PNG,
   on a page where every other surface is the v2 ladder.

   This is the HOME PAGE'S review card (Part 6.5), reached through the
   theme's own .testimonial.classic markup instead of the pv2p classes —
   same raised fill, same serif gold quote mark, same hairline above the
   reviewer's name — so the two sections can never drift apart again.
   The avatar PNG and the italic go with the white card: the home
   treatment has neither.

   Specificity: essentials' `.testimonial.classic p` and `.testimonial
   .male p` are (0,2,1)/(0,2,1); every rule here carries body.pubv2 +
   two classes and wins. `.testimonial { margin-bottom: 60px }` is
   brought onto the rhythm scale (26px, --pv2p-s5).
   ------------------------------------------------------------ */
body.pubv2 .testimonial.classic {
	position: relative;
	margin: 0 0 var(--pv2p-s5);
	background: var(--pv2-raised);
	border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius-lg);
	padding: 16px 20px 14px 56px;
}
body.pubv2 .testimonial.classic:before {
	content: '\201C';
	position: absolute; left: 17px; top: 7px;
	font-family: Georgia, 'Times New Roman', serif;
	font-size: 46px; line-height: 1;
	color: var(--pv2-gold); opacity: 0.85;
}
body.pubv2 .testimonial.classic p {
	background: none;              /* kills the #F6F6F6 card AND the avatar PNG */
	color: var(--pv2-text-soft);
	font-style: normal;
	font-size: 14px; line-height: 1.6;
	margin: 0; padding: 0;
	border-radius: 0;
}
body.pubv2 .testimonial.classic cite {
	display: block;
	margin: 12px 0 0; padding: 10px 0 0;
	background: none;
	border-top: 1px solid var(--pv2-line);
	border-radius: 0;
	font-style: normal; font-size: 12px; color: var(--pv2-text-muted);
}
body.pubv2 .testimonial.classic cite strong { color: #fff; font-weight: 600; }

/* ------------------------------------------------------------
   5.6c  INLINE <code> — Bootstrap's pink-on-white chip survives inside
         the dark FAQ answers on seven game pages (aloft, aska,
         astro-colony, conan-exiles-enhanced, crosswind and siblings):
         measured rgb(249,242,244) background with rgb(199,37,78) text on
         a #1a1a1b page. The v2 chip is the field surface with the
         panel's mono stack.
   ------------------------------------------------------------ */
body.pubv2 code {
	background: var(--pv2-field);
	color: var(--pv2-accent);
	border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius);
	padding: 1px 6px;
	font-family: var(--pv2-mono);
	font-size: 12.5px;
}

/* ------------------------------------------------------------
   5.6d  THE NOTIFY EMAIL CAPTURE — the OLD variant. 16 templates (ark,
         ark2, palworld, enshrouded, light-no-fire, sons-of-the-forest &
         siblings) ship `.col-sm-9 > input.form-control.input-sm` — a
         stock-Bootstrap WHITE text input on a dark band, the last white
         control on those pages. (The newer archetype-D notify pages are
         already dark via inline styles and are not touched; neither is
         the green "Join Notify List" submit — loud but consistent across
         all 16, which is a decision, not a defect. See 1b.)
         Colour only: height, padding and layout stay Bootstrap's.
   ------------------------------------------------------------ */
body.pubv2 .col-sm-9 > input.form-control[type="email"] {
	background: var(--pv2-field);
	border: 1px solid var(--pv2-border);
	color: #fff;
	box-shadow: none;
}
body.pubv2 .col-sm-9 > input.form-control[type="email"]::placeholder {
	color: var(--pv2-text-muted);
	opacity: 1;
}
body.pubv2 .col-sm-9 > input.form-control[type="email"]:focus {
	border-color: var(--pv2-border-strong);
	outline: 0;
}

/* ------------------------------------------------------------
   5.7  THE FAQ ACCORDION on the theme's RAW div.toggle markup — 294 files
        plus /affiliates/.

        div.toggle.transparent > div.toggle(.active) > label + .toggle-content

   This is the same component .pv2p-acc styles, reached without the class,
   so the two must not both fire: `:not(.pv2p-acc)` / `:not(.pv2p-acc-item)`
   keep /faq/ and /jobs/ — which DO carry the classes — on the Part 2
   component. Everything Part 2 says about this component still holds:

     - NEVER set `display` on .toggle-content. slideDown/slideUp write an
       inline display and an !important here would freeze a panel open.
     - The open state is the THEME's `.active`, not one of ours.
     - The chevron is drawn from borders. fa-subset ships no chevron-down,
       angle-down, plus or minus at all, so a glyph would render as
       literally nothing and would not show up in a diff.

   MEASURED FAULT: the theme's label is `padding: 10px 20px` and draws its
   triangle at `right: 14px`, so a long question runs under its own
   chevron — reproduced at 390 on the live ASKA page, question text ending
   at x=352 with the chevron box starting at x=349. The label now reserves
   46px on the right, which is the whole fix and it is width-independent.
   ------------------------------------------------------------ */
/* THE MARGIN MOVED FROM BOTTOM TO TOP, AND THAT IS THE WHOLE FIX FOR THE
   HEADING INTERVAL. On a game page the FAQ is not one accordion: it is a
   RUN of `div.toggle.transparent` wrappers, one question each. So a
   `margin-top` on the first card cannot be targeted without a positional
   selector, which PART 5 bans — but moving the existing 10px from bottom
   to top does it for free: card-to-card is still 10px (it is now the
   NEXT card's top instead of the previous card's bottom), and the first
   card gains 10px of air from the section heading above it.
   MARGIN COLLAPSE IS WHY THE FIRST ATTEMPT DID NOTHING. `section >
   .container > header` already carries `margin-bottom: --pv2p-s5` (26px)
   and the wrapper's 10px top collapsed INTO it — max(26,10) = 26, so the
   measured gap stayed at 27. The interval only moves if the wrapper's own
   top margin is the larger of the two. --pv2p-s6 (36) is the section
   interval the rest of the page uses, so that is the number.

   The 10px card-to-card gap is then restored by the adjacent-sibling rule
   below. `+` between two elements of the SAME class in the same parent is
   exact — it is not the positional selector PART 5 bans, which is about
   guessing WHICH SECTION you are in across 64 orderings.

   Measured on 7DTD at 1920: heading -> first card 27px -> 36px,
   card -> card unchanged at 10px. */
body.pubv2 div.toggle.transparent:not(.pv2p-acc) {
	margin: var(--pv2p-s6) 0 0;
	background: var(--pv2-panel);
	border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius-lg);
	overflow: hidden;
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) + div.toggle.transparent:not(.pv2p-acc) {
	margin-top: var(--pv2p-s2);
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item) {
	margin: 0; position: relative; clear: none;
	border-top: 1px solid var(--pv2-line);
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item):first-child { border-top: 0; }
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item) > label {
	position: relative; display: flex; align-items: center;
	/* --pv2p-pad, not the flat --pv2p-s4 it used to be: /faq/'s copy of
	   this component steps its label to 26px at 1400 and the game-page
	   copy did not, so the same accordion had two different insets on the
	   same screen. One token now. */
	margin: 0; padding: 14px 46px 14px var(--pv2p-pad); min-height: 52px;
	background: transparent; border: 0; border-bottom: 0; border-radius: 0;
	color: #fff; font-family: var(--pv2p-font);
	font-size: 14.5px; font-weight: 600; line-height: 1.45;
	cursor: pointer; user-select: none;
	transition: background-color .12s, color .12s;
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item) > label:hover {
	background: rgba(255, 255, 255, 0.03); color: var(--pv2-gold);
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item) > label:focus-visible {
	outline: 2px solid rgba(213, 168, 74, .45); outline-offset: -2px;
}
/* the theme draws a CSS triangle on :before in both states — repurpose it
   as the gold open-state edge, exactly as .pv2p-acc does */
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item) > label:before {
	content: ''; position: absolute; left: 0; top: 0; bottom: 0; right: auto;
	width: 3px; height: auto; border: 0; margin: 0;
	background: transparent; transition: background-color .14s;
}
/* full-height open edge — see the note on .pv2p-acc-item.active in PART 2 */
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle.active:not(.pv2p-acc-item) {
	box-shadow: inset 3px 0 0 var(--pv2-gold);
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle.active:not(.pv2p-acc-item) > label:before {
	background: transparent; border: 0; margin: 0;
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle.active:not(.pv2p-acc-item) > label {
	color: var(--pv2-gold); background: rgba(234, 168, 36, 0.045);
}
/* the chevron */
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item) > label:after {
	content: ''; position: absolute; right: 21px; top: 50%;
	width: 8px; height: 8px; margin-top: -6px;
	border: solid var(--pv2-text-muted); border-width: 0 2px 2px 0;
	transform: rotate(45deg); transform-origin: 50% 50%;
	transition: transform .18s ease, border-color .12s;
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle.active:not(.pv2p-acc-item) > label:after {
	border-color: var(--pv2-gold); margin-top: -2px; transform: rotate(225deg);
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item) > .toggle-content {
	margin: 0; padding: var(--pv2p-s3) var(--pv2p-pad) var(--pv2p-s4);
	background: transparent; border: 0; border-radius: 0;
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) .toggle-content p,
body.pubv2 div.toggle.transparent:not(.pv2p-acc) .toggle-content li {
	font-size: var(--pv2p-fs-body); line-height: var(--pv2p-lh-body);
	color: var(--pv2-text-soft);
}
body.pubv2 div.toggle.transparent:not(.pv2p-acc) .toggle-content > *:last-child { margin-bottom: 0; }
@media (prefers-reduced-motion: reduce) {
	body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item) > label:after { transition: none; }
}

/* ------------------------------------------------------------
   5.8  THE FLEXSLIDER — CHROME ONLY. Read this before adding anything.

   The design contract says do not write a sitewide .flexslider rule, and
   that stands for GEOMETRY: the plugin writes slide widths, clone counts,
   float and inline `width` on `.slides > li` at runtime, and a shared
   sheet that argues with them loses in a way nobody notices until a
   carousel with a different slide count appears.

   NOTHING BELOW TOUCHES GEOMETRY. No rule here mentions .slides, .slides
   li, width, float, display or position on any element the plugin owns.
   It is the paging dots and the slide frame — chrome the plugin paints
   once and never recalculates. Keep it that way, or move it to a
   page-scoped skin.

   Why it is worth the exception: `.flexslider.flexFull` is on 293 of 295
   game pages plus the home page, and its paging dots measure 11 x 11 —
   under the 44px floor on every one of them.

   THE HIT RULE, third case again: the dots are a horizontal run at a 23px
   pitch (11px dot + 6px margin a side). Growing them apart would move the
   control strip; a 44px-wide extender would overlap its neighbours by
   21px and make every dot fire the wrong slide. So each dot keeps its
   11px mark and takes a 23 x 44 hit CELL — exactly the pitch, so the
   cells tile with zero overlap and the whole vertical band is live.
   ------------------------------------------------------------ */
body.pubv2 .flexslider .flex-control-paging li a {
	position: relative;
	background: rgba(255, 255, 255, 0.32);
	box-shadow: none;
}
body.pubv2 .flexslider .flex-control-paging li a:hover { background: rgba(255, 255, 255, 0.55); }
body.pubv2 .flexslider .flex-control-paging li a.flex-active {
	background: var(--pv2-gold); box-shadow: none;
}
@media (hover: none) and (pointer: coarse), (max-width: 900px) {
	body.pubv2 .flexslider .flex-control-paging li a:after {
		content: ''; position: absolute;
		left: -6px; right: -6px; top: 50%; height: 44px;
		transform: translateY(-50%); pointer-events: auto;
	}
}

/* ------------------------------------------------------------
   5.9  THE ORDER WIDGET — themes/epona/tpl/order_widget.tpl
        ONE template, printed into 245 of the 295 game pages.

   The single most commercially important element on the public site: the
   slot / cycle / location selector, the price, the coupon chip and the
   Order button. Its own class vocabulary is server-injected, which is why
   none of it appears in the audit's shared-class tiers, and it carries its
   own self-contained <style> block inside the template.

   WHY THE SKIN LIVES HERE AND NOT IN THAT <style> BLOCK: the template
   renders for EVERY visitor, staff or not. A rule written there ships to
   customers the moment it deploys, outside the gate, before Ryan has seen
   any of this. Everything below is `body.pubv2`-scoped in the gated
   sheet, so the widget a customer sees today is byte-identical and the
   redesign lands with the rest of the preview.

   Specificity note: the template's block is inline in <body> and
   therefore WINS TIES against this file, which header.tpl links earlier.
   Every rule below is body.pubv2 + >= 2 classes, and the handful of
   template declarations carrying !important are matched with !important
   and named where that happens.

   THE FOUR MEASURED FAULTS, all at 390 on four live pages, identical on
   every one because it is one file:

   1. SPAN.loc-name renders 24px wide where it needs 65 — every card reads
      "L...". On the gold card it renders at ZERO: "Los Angeles" is gone
      entirely and the button reads "(flag) INSTANT 15ms". A customer
      cannot tell which datacenter they are buying in, on the conversion
      surface, on 245 pages.
   2. The latency badge overruns the caret — measured right edge 350 vs
      caret left edge 340 on the gold card. That is the "11m(v)" render.
   3. The price block is a fixed 28px box holding 37px of content: the
      period wraps to a second line and is CLIPPED, so "first Month"
      breaks and misaligns under the figure.
   4. Every control is under the touch floor: Order Now 93x39, the
      location button 144x37, the premium toggle 92x43.

   Faults 1-3 are all the same root cause: `.btn-text` is a flex row of
   flag + name + INSTANT + latency in 128px of usable width, and the name
   is the only item with flex-shrink, so the name absorbs 100% of the
   deficit and the badges overflow the box. The fix gives the name its own
   line and lets the badges have the next one — nothing is hidden, nothing
   is truncated, and the button gets taller, which is where the 44px floor
   comes from as well.
   ------------------------------------------------------------ */

/* ---------- WHAT THIS SECTION DOES NOT DO ----------
   REVERTED 2026-08-04, Ryan: "I don't like that you resized all of the
   pricing cards. You did a perfect job before. They're supposed to be
   different sizes and have different glow."

   The four tier treatments — the differing card widths and min-heights, the
   staggered bottom-aligned tops, and the green / gold / purple / grey glow
   with its matching gradient Order button — are a DESIGN DECISION Ryan had
   already signed off, not a defect. They were flattened here into uniform
   dark chrome with one gold button and equal heights, and that was wrong.
   Everything that did it is gone: no background, no border, no box-shadow,
   no z-index, no align-items, no per-tier colour, no h3 sizing, no
   badge/price/step recolouring, and the button's hover lift is the
   template's again.

   THE RULE THAT COMES OUT OF IT, and it governs the whole shared layer:
   FIX WHAT IS BROKEN, DO NOT RESTYLE WHAT IS CHOSEN. A treatment that is
   loud but consistent and deliberate is a decision. "It does not match the
   token palette" is not a defect report. The order widget is the one
   surface on the public site where that mistake costs revenue.

   What is left below is only the measured breakage, and every rule carries
   the measurement it fixes. If a rule here cannot name a number it broke,
   it does not belong in this file.
   ---------------------------------------------------------- */

/* the card can be handed a location name longer than its own box */
body.pubv2 .pricing-card { overflow-wrap: break-word; }

/* ---------- the location control ----------
   FIX 1: SPAN.loc-name rendered 24px wide needing 65 — every card read
   "L..." — and 0px on the recommended card, where "Los Angeles" was gone
   entirely. FIX 2: the latency badge overran the caret (right edge 350 vs
   caret left 340) and rendered as "11m(v)". Both are the same cause: the
   toggle is a flex row of flag + name + INSTANT + latency in 128px, and
   the name is the only item with flex-shrink, so it absorbs 100% of the
   deficit and the badges overflow the box.

   Geometry only. Not one colour, radius, weight or size below. */
body.pubv2 .pricing-card .location-select { width: 100%; }   /* was 95%: +8px for the name */
body.pubv2 .pricing-card .location-dropdown .dropdown-toggle {
	/* 8px, not the template's 10px. Measured on the xsmall card at 1440:
	   flag 20 + gap 6 + "Los Angeles" 74 + gap 6 + bolt 10 + 4 + latency 34
	   is exactly 2px more than the row had, so the name ellipsised to 72.
	   Four pixels back off the horizontal padding clears it with room. */
	padding: 6px 8px;
}
/* the caret keeps a hard gap of its own so nothing can park on it again,
   whatever the widget's JS injects into the row */
body.pubv2 .pricing-card .location-dropdown .dropdown-toggle .caret { margin-left: 8px; }
body.pubv2 .pricing-card .location-dropdown .latency-label { margin-left: 4px; }
/* FIX 3: a long city name touched the INSTANT badge in the menu rows —
   "Los Angeles" and "Singapore" at 390 had zero px between them. */
body.pubv2 .pricing-card .location-dropdown .dropdown-menu li a { column-gap: 8px; }
body.pubv2 .pricing-card .location-dropdown .dropdown-menu li a .loc-left { min-width: 0; }

/* ---------- the price ----------
   FIX 4: `.price` is a fixed 28px box and the discounted row measured 37px
   at 390, so the period wrapped INSIDE the box and was clipped — "first
   Month" broke across two lines and misaligned under the figure. Wrapping
   as whole units is the fix; the box goes auto-height at <=560 below. */
body.pubv2 .pricing-card .price { flex-wrap: wrap; row-gap: 0; }
body.pubv2 .pricing-card .price .original,
body.pubv2 .pricing-card .price .discounted,
body.pubv2 .pricing-card .price .period { white-space: nowrap; }

/* ---------- PHONE + SMALL TABLET: the 2x2 grid ----------
   The grid itself is the template's and stays: four tiers above the fold
   in two rows is the product decision and it is not a defect. What
   changes is that the location control stops trying to fit a flag, a
   68px city name, an INSTANT badge, a latency badge and a caret into
   128px of one line. */
@media only screen and (max-width: 990px) {
	/* THE ONE LINE THAT UN-CLIPS THE RECOMMENDED CARD. At these widths the
	   large card is exactly as narrow as its three siblings, and the
	   template ALREADY collapses "INSTANT" to a bolt on those three — it
	   just never did it on the fourth. Extending the template's own mark to
	   the card it forgot is what takes loc-name from 0px to legible. */
	body.pubv2 .pricing-card-large .location-dropdown .btn-text .instant-label { font-size: 0; margin-left: auto; }
	body.pubv2 .pricing-card-large .location-dropdown .btn-text .instant-label:before { content: '\26A1'; font-size: 10px; line-height: 1; }
}
@media only screen and (max-width: 560px) {
	/* THE NAME OWNS THE FIRST LINE. flex-basis:100% on the badge forces the
	   break deterministically instead of leaving it to whatever the name
	   happens to measure, so no location string can ever push the badges
	   over the caret again. Nothing is hidden and nothing is truncated. */
	body.pubv2 .pricing-card .location-dropdown .dropdown-toggle { align-items: flex-start; padding: 7px 9px; }
	body.pubv2 .pricing-card .location-dropdown .btn-text { flex-wrap: wrap; row-gap: 3px; }
	body.pubv2 .pricing-card .location-dropdown .btn-text .loc-name {
		flex: 1 1 auto; min-width: 0; font-size: 12.5px;
	}
	body.pubv2 .pricing-card .location-dropdown .btn-text .instant-label {
		flex: 0 0 100%; margin-left: 0; order: 2;
	}
	body.pubv2 .pricing-card .location-dropdown .btn-text .latency-label { order: 3; margin-left: 5px; }
	body.pubv2 .pricing-card .location-dropdown .dropdown-toggle .caret { margin-top: 5px; }

	/* the menu is wider than the card it hangs off. Left-aligned it runs
	   past the viewport from the right-hand column; right-aligning the
	   even cards keeps both columns' menus fully on screen at 390. The
	   container holds exactly four cards, so even == right column. */
	body.pubv2 .pricing-cards-container > .pricing-card:nth-child(even) .location-dropdown .dropdown-menu {
		left: auto; right: 0;
	}
	body.pubv2 .pricing-card .location-dropdown .dropdown-menu { min-width: 196px; max-width: calc(100vw - 30px); }
	/* the menu rows take the same collapsed badge the toggle does at this
	   width — a 196px row cannot hold flag + city + "INSTANT" + latency,
	   and the CITY is what a customer picks by. Same mark, same meaning. */
	body.pubv2 .pricing-card .location-dropdown .dropdown-menu li a .instant-label { font-size: 0; }
	body.pubv2 .pricing-card .location-dropdown .dropdown-menu li a .instant-label:before { content: '\26A1'; font-size: 11px; line-height: 1; }

	/* the price: a fixed 28px box was clipping 37px of content. Height only
	   — the template owns the type size here and keeps it. */
	body.pubv2 .pricing-card .price { height: auto; min-height: 28px; }

	/* the CTA fills its card. 93x39 in a 176px card is under the touch floor
	   and leaves the card looking unfinished; the tier's own gradient,
	   padding and radius are untouched. */
	body.pubv2 .pricing-card .btn-order { width: 100%; }
}

/* ---------- DESKTOP: the four cards fill the wide rail ----------
   Ryan, 2026-08-04: "we could expand each of the cards width a little bit
   so it fills out the gaps left and right."

   MEASURED at 1920 before: the four tiers cap at 200 + 230 + 250 + 290
   (+ 3x15 gaps) = 1015px of row centred in a 1500px band container —
   242px of dead band either side of the single most commercially
   important element on the site, ever since 5.0 widened the rail.

   ONLY THE PER-TIER max-width CAPS MOVE, all four scaled by the same
   ~1.45. The flex ratios (0.7 / 0.9 / 0.95 / 1.15), paddings, min-heights,
   staggered bottom-aligned tops and the four glows are the template's and
   stay untouched (§1b: the tier treatment is chosen, not broken) — the
   cards remain deliberately different sizes, just scaled to the rail they
   sit in. At 1200-1399 the flex shares stay below even the old caps for
   xsmall/small/medium, so the row simply relaxes to its container; at
   <=1199 the template's own tablet/phone layouts are in charge and
   nothing here matches. The template's <style> declares the same
   property at (0,1,0) with no !important; this is (0,2,0) and wins. */
@media only screen and (min-width: 1200px) {
	body.pubv2 .pricing-card-xsmall { max-width: 290px; }
	body.pubv2 .pricing-card-small { max-width: 335px; }
	body.pubv2 .pricing-card-medium { max-width: 365px; }
	body.pubv2 .pricing-card-large { max-width: 420px; }
}

/* ------------------------------------------------------------
   5.10  THE GAME-PAGE TOUCH FLOOR — 44px.
   Doubled query for the same reason the panel files double theirs:
   headless Chromium drops `hover: none` after the first navigation in a
   Playwright context, so a pointer-only rule ships unverified.

   Measured at 390 on four live game pages before this block: 118-124 of
   the 121-127 visible interactive elements were under 44px.
   ------------------------------------------------------------ */
@media (hover: none) and (pointer: coarse), (max-width: 900px) {
	/* the tier's gradient, padding, radius and hover lift are the
	   template's and stay — inline-flex only so `min-height` can raise a
	   39px button to the floor and keep its label centred while it does. */
	body.pubv2 .pricing-card .btn-order {
		display: inline-flex; align-items: center; justify-content: center;
		min-height: 44px; width: 100%;
	}
	body.pubv2 .pricing-card .location-dropdown .dropdown-toggle { min-height: 44px; }
	body.pubv2 .pricing-card .location-dropdown .dropdown-menu li a { min-height: 44px; }
	body.pubv2 .premium-toggle-btn { min-height: 44px; }
	/* the callout CTA — .btn-lg is already full width below 990 in the
	   theme, so this is the height only */
	body.pubv2 .callout .btn { min-height: 44px; }
	/* the FAQ rows */
	body.pubv2 div.toggle.transparent:not(.pv2p-acc) > div.toggle:not(.pv2p-acc-item) > label { min-height: 48px; }
	/* the eight location links. GROW THE ROW, do not extend the hit area:
	   this is a dense vertical list and a 44px extender centred on a 28px
	   pitch would overlap its neighbours by 8px, so the top of every link
	   would fire the link above it. Same call as the footer link list. */
	body.pubv2 ul.list-icon.angle-right > li > a { min-height: 44px; }
	body.pubv2 ul.list-icon.angle-right > li { margin-bottom: 0; }
	body.pubv2 ul.list-icon.angle-right > li:before { margin-top: 18px; }
	/* the {wiki_guides} block — a SMARTY PLUGIN, not a template
	   (includes/libraries/smarty/plugins/function.wiki_guides.php), on
	   ~250 game pages. It ships its own <style> and is already written to
	   the v2 palette in hex, so it needs nothing but the touch floor:
	   measured 288x39 / 259x39 nodes and a 339x23 "more" link at 390.
	   The nodes are a wrapping flex grid that stacks one per line on a
	   phone, so growing the row is the correct call here too. */
	body.pubv2 .ss-wg .ss-wg-node { min-height: 44px; }
	body.pubv2 .ss-wg .ss-wg-more { display: inline-flex; align-items: center; min-height: 44px; }

	/* THE GAMES BROWSER's two controls — /services/game_servers/, the index
	   of all 295. Measured at 390 and 768: the sort button 34x126 and the
	   search field 34x180, the only two interactive elements on the page
	   that are not a game link, and the only two sub-44 targets it has once
	   the footer is fixed. `.sort-controls` / `.search-box` / #gamesPageSort
	   are layout.css classes shared with the PANEL's order page — which is
	   never body.pubv2 (the gate's allowlist is public routes only), so
	   this cannot reach it. Height only: no colour, no padding, no radius.
	   `.input-sm` sets a 30px line-height that would keep the field short
	   inside a taller box, so it is zeroed to the control height. */
	body.pubv2 .sort-controls .dropdown-toggle {
		display: inline-flex; align-items: center;
		min-height: 44px;
	}
	body.pubv2 .search-box #gamesPageSort { height: 44px; line-height: normal; }
}

/* ------------------------------------------------------------
   5.11  THE COUPON / MESSAGE BANNER — `.alert`, essentials.css section 14.

   RYAN, 2026-08-05: the "Coupon code PALSHROUDED activated!" banner "is the
   2013 alert — cream-yellow, full-bleed, enormous, two centred lines against a
   dark page."

   It is exactly that: `.alert.alert-warning { background:#fce3a3 }` with a 15px
   line, no radius, no width cap, so on a game page it is a full-rail block of
   daylight sitting above the first dark band.

   ONE FIX WHERE IT LIVES. `.alert` is shared — it is the coupon banner on ~229
   game pages, and it is the error banner on the order page — so this restyles
   the component rather than the coupon, and every variant keeps its own meaning
   through the accent edge instead of through a pastel fill. Same panel
   vocabulary as everything else in this sheet: dark fill, hairline, 8px radius,
   3px inset accent edge.

   NOTHING HERE SETS `display`. There is a global
   `.alert-success { display: none !important }` on the order page (see the
   trap list in Part 0) and that behaviour must survive untouched.

   229 of the game pages carry the banner as `<div class="col-md-6 alert ...">`,
   so bootstrap's grid float and 50% width are neutralised first — otherwise the
   width cap lands on a floated half-rail and the centring does nothing.
   ------------------------------------------------------------ */
body.pubv2 .alert {
	position: relative;
	max-width: 720px;
	margin: 0 auto var(--pv2p-s3);
	padding: 9px 32px 10px 15px;
	background: rgba(0, 0, 0, 0.32);
	border: 1px solid var(--pv2-border);
	border-radius: 8px;
	box-shadow: inset 3px 0 0 rgba(242, 197, 34, 0.85);
	color: var(--pv2-text-soft);
	font-size: 13px;
	line-height: 1.5;
	text-align: left;
}
body.pubv2 [class*="col-"].alert { float: none; width: 100%; }
body.pubv2 .alert.alert-success { box-shadow: inset 3px 0 0 rgba(46, 224, 110, 0.80); }
body.pubv2 .alert.alert-info    { box-shadow: inset 3px 0 0 rgba(88, 166, 255, 0.80); }
body.pubv2 .alert.alert-warning { box-shadow: inset 3px 0 0 rgba(242, 197, 34, 0.85); }
body.pubv2 .alert.alert-danger  { box-shadow: inset 3px 0 0 rgba(226, 90, 90, 0.85); }
body.pubv2 .alert strong { color: #fff; font-weight: 600; }
/* the coupon CODE, which the markup wraps in <em> */
body.pubv2 .alert em { font-style: normal; font-weight: 700; color: #f2c522; }
body.pubv2 .alert a { color: #8ab4f8; }
body.pubv2 .alert i.fa { font-size: 14px; margin-right: 8px; }
body.pubv2 .alert .close {
	position: absolute; right: 9px; top: 6px;
	color: var(--pv2-text-soft); opacity: .5;
	font-size: 17px; font-weight: 400; text-shadow: none;
}
body.pubv2 .alert .close:hover,
body.pubv2 .alert .close:focus { color: #fff; opacity: 1; }

/* ------------------------------------------------------------
   5.12  THE LAUNCHER SHOWCASE — Hearth / Beacon / Lantern / Sundial /
         Cauldron / Lodestone, one block copied six times.

   RYAN, 2026-08-05: "the launcher showcase is way too big - the 'Powered by
   Hearth' image renders taller than a full laptop viewport."

   Measured: the shot was 1120x720, the block has no width cap, so at a 1420px
   rail it rendered ~1392x895 — the heading scrolled off before the image ended.

   The cap goes on the BLOCK, not the image, so the frame, its border and the
   caption scale with it and the whole unit stays one object. 760px puts the
   re-shot 2240x1240 picture at ~732x405 and the heading, the shot and the
   caption inside a 768px-tall viewport together.

   The `max-height` on the image is the belt-and-braces: the width cap only
   bounds the height because the aspect is known, and these shots have already
   been re-cut once. If a future capture comes back taller than 16:9 it gets
   contained rather than pushing the caption out of the fold again.

   The templates own these classes in their own <style>, and those blocks also
   render for a classic visitor — so the cap lives here, gated, instead of in
   the six templates.
   ------------------------------------------------------------ */
body.pubv2 .hearth-showcase,
body.pubv2 .beacon-showcase,
body.pubv2 .lantern-showcase,
body.pubv2 .sundial-showcase,
body.pubv2 .cauldron-showcase,
body.pubv2 .lodestone-showcase {
	max-width: 760px;
	margin-left: auto;
	margin-right: auto;
}
body.pubv2 .hearth-showcase-frame > img,
body.pubv2 .beacon-showcase-frame > img,
body.pubv2 .lantern-showcase-frame > img,
body.pubv2 .sundial-showcase-frame > img,
body.pubv2 .cauldron-showcase-frame > img {
	max-height: 470px;
	object-fit: contain;
}

/* ------------------------------------------------------------
   5.12b  THE VALHEIMONE SHOWCASE — `.vo-showcase-slider`, the Valheim page.

   RYAN, 2026-08-05: "no section taller than a normal laptop screen. The
   ValheimOne block is the same defect as the launcher showcase."

   It is the same defect and it takes the same fix. Measured at 1440x790
   before this block: the slider had no width cap, so on the 1260px rail the
   shots rendered 1228x849 and the carousel ALONE was taller than the whole
   viewport — the "Powered by ValheimOne" heading had scrolled off before the
   first screenshot finished. Section total 1523px.

   Same numbers as 5.12 on purpose: 760px on the block (so the frame, its
   border, the caption and the paging dots scale as one object) and 470px on
   the shot as the belt-and-braces for a future re-capture that comes back
   taller than 16:9. That puts heading + shot + caption inside one 790px fold,
   which is what 5.12 bought the six launcher pages.

   Gated here rather than in the template's own <style>, because that <style>
   also renders for a classic visitor and the classic page must not move.
   ------------------------------------------------------------ */
body.pubv2 .vo-showcase-slider {
	max-width: 760px;
	margin-left: auto;
	margin-right: auto;
}
body.pubv2 .vo-showcase-frame > img {
	max-height: 470px;
	object-fit: contain;
}

/* ---- 5.12b (ii)  THE REST OF THE VALHEIMONE BAND.
   Capping the shot was not enough. Measured after 5.12b, at 1440x790:
     header 253  (h2 38, sub-line 54, badge row 41, GitHub link 22,
                  three-line legal disclaimer 54, plus 44px of gaps)
     showcase 580
     -> 833 before the cards, so the "Powered by ValheimOne" heading still
        scrolled off before the caption arrived. Section total 1251px.

   The launcher sections that Ryan accepted run header 73 + showcase 505 = 578
   inside a 970px section, and the ONLY structural difference is that this band
   puts a badge row, a GitHub link and the third-party disclaimer between the
   heading and the picture. So the header comes down to fine-print weight and
   the shot to the launcher's own size, and the unit lands in one fold.

   Measured after: header 189 + showcase 478 = 667, inside BOTH 1440x790 and
   1280x720; section total 1017px, in line with the launcher bands.

   The disclaimer is not removed and not reworded - it is set at fine-print size
   over a wider measure, which is what it is. Gated, because the template's own
   <style> renders for a classic visitor too and the classic page must not move.
   ---- */
body.pubv2 .vo-section { padding-top: 38px; padding-bottom: 38px; }
body.pubv2 .vo-section > .container > header.text-center { margin-bottom: 10px; }
body.pubv2 .vo-section > .container > header.text-center > h3.h4 {
	margin-top: 7px !important;
	font-size: 14.5px !important;
	line-height: 1.45 !important;
	max-width: 760px !important;
}
body.pubv2 .vo-section .vo-badges { margin-top: 2px; }
body.pubv2 .vo-section .vo-badges span { padding: 3px 11px; font-size: 11.5px; margin-top: 4px; }
body.pubv2 .vo-section > .container > header.text-center > p { margin-top: 7px !important; font-size: 13px; }
body.pubv2 .vo-section > .container > header.text-center > p:last-child {
	max-width: 1100px !important;
	font-size: 10.5px !important;
	line-height: 1.4 !important;
	margin-top: 7px !important;
}
/* the slider carries a max-width from the template's own <style>, so this one
   needs !important to land - the frame, its border, the caption and the paging
   dots all scale with the block, which is why the cap goes here and not on img */
body.pubv2 .vo-showcase-slider {
	max-width: 620px !important;
	margin-top: 14px !important;
	margin-bottom: 18px !important;
}
body.pubv2 .vo-showcase-caption { padding: 10px 14px 14px !important; font-size: 12.5px !important; }

/* An icon chip inside a card HEAD bar, not stacked above a title in the body.
   .pv2p-icon's `margin: 0 0 14px` is right when it sits above a heading; in a
   head bar that 14px lands inside the h4's line box, inflating it to 52px and
   the bar to 77px, so the icon reads as jammed against the text and floating
   off-centre in a too-tall row. Measured: bar 77px, h4 line box 52px for 15px
   text. In a head bar the chip is a sibling of the words, so: no bottom margin,
   a real gap beside it, and both centred on the same line. */
body.pubv2 .pv2p-chead h4 { display: flex; align-items: center; gap: 11px; margin: 0; line-height: 1.3; }
body.pubv2 .pv2p-chead .pv2p-icon { margin: 0; flex: 0 0 auto; }

/* ============================================================
   ============================================================
   PART 6 — THE HOME PAGE  (body.pubv2.pv2home)
   ============================================================

   The page-scoped layer index.tpl's `pv2home` body class exists for.
   Everything here is scoped body.pubv2.pv2home, so no other page —
   converted or not — can inherit a rule from this section. The home page
   is one known DOM, which is why positional selectors (banned in Part 5's
   64-ordering game corpus) are legitimate here, same as inside our own
   grid components.

   WHAT THIS SECTION ANSWERS, each from a Ryan note on the preview:
     - "the features list is bland and stale" / "monotonous, one long
       uniform list" -> 6.2 and 6.4: the six welcome cards and the three
       24-item checklist columns stop being identical grey runs. The
       accent vocabulary is the pw-feat treatment Ryan approved on the
       Palworld page (green / blue / gold / purple chips + inset edges +
       the gold-circle tick), not an invented palette.
     - "the transition from the dark customer testimonials to the reviews
       looks really weird... more pizzazz" -> 6.5: the testimonials band
       becomes a designed surface (panel rung + a gold bloom rising from
       its bottom edge into the footer's gold hairline) and the six quotes
       become real review cards. Root cause of "weird" was also 6.1: the
       quotes ship `animate_from_bottom`, essentials.css holds them at
       opacity 0 (+50px offset) until jQuery .appear() fires, so the band
       rendered as a heading over a black void until you scrolled just so.
     - "the computer on the right needs to show the updated control
       panel" -> the images are swapped in index.tpl (gated); 6.3 widens
       the figure so the v2 panel shot is actually readable, and 6.1 stops
       the screenshot depending on the scroll-reveal to exist.
   ============================================================ */

/* ------------------------------------------------------------
   6.1  DETERMINISTIC REVEAL — the scroll-fade classes stay in the markup
        (classic keeps its animation), but on the v2 page the content
        simply exists. essentials.css:193/217 hold these at opacity 0
        (+ a 50px offset) and ONLY jQuery .appear() ever reveals them —
        measured on the preview: the control-panel screenshot and all six
        testimonials render as nothing until their scroll trigger fires.
        The jQuery reveal still runs and animates to these exact end
        values, so it becomes a no-op rather than a conflict.
   ------------------------------------------------------------ */
body.pubv2.pv2home .animate_fade_in { opacity: 1; right: 0; }
body.pubv2.pv2home .animate_from_bottom { opacity: 1; bottom: 0; }

/* ------------------------------------------------------------
   6.2  THE SIX WELCOME CARDS — differentiated, not six copies.
        The accent set is the Palworld pw-feat vocabulary (approved):
        blue #58a6ff, green #2ee06e, purple #9b5cf6, gold #f2c522.
        Each card gets a 2px accent top edge (drawn on the chead, which
        only the welcome cards have — the testimonial grids share
        .pv2p-cell and must not match) and a matching icon chip. Laid so
        no colour repeats beside or above itself:
          row 1: blue (Support) green (Forums) purple (Wiki)
          row 2: gold (Instant Setup) blue (Upgrade) green (Affiliate)
   ------------------------------------------------------------ */
body.pubv2.pv2home .pv2p-cell:nth-child(1) .pv2p-chead { box-shadow: inset 0 2px 0 rgba(88, 166, 255, 0.55); }
body.pubv2.pv2home .pv2p-cell:nth-child(2) .pv2p-chead { box-shadow: inset 0 2px 0 rgba(46, 224, 110, 0.50); }
body.pubv2.pv2home .pv2p-cell:nth-child(3) .pv2p-chead { box-shadow: inset 0 2px 0 rgba(155, 92, 246, 0.55); }
body.pubv2.pv2home .pv2p-cell:nth-child(4) .pv2p-chead { box-shadow: inset 0 2px 0 rgba(242, 197, 34, 0.55); }
body.pubv2.pv2home .pv2p-cell:nth-child(5) .pv2p-chead { box-shadow: inset 0 2px 0 rgba(88, 166, 255, 0.55); }
body.pubv2.pv2home .pv2p-cell:nth-child(6) .pv2p-chead { box-shadow: inset 0 2px 0 rgba(46, 224, 110, 0.50); }
body.pubv2.pv2home .pv2p-cell:nth-child(1) .pv2p-chead .pv2p-icon,
body.pubv2.pv2home .pv2p-cell:nth-child(5) .pv2p-chead .pv2p-icon { background: rgba(88, 166, 255, 0.10); border-color: rgba(88, 166, 255, 0.32); color: #58a6ff; }
body.pubv2.pv2home .pv2p-cell:nth-child(2) .pv2p-chead .pv2p-icon,
body.pubv2.pv2home .pv2p-cell:nth-child(6) .pv2p-chead .pv2p-icon { background: rgba(46, 224, 110, 0.10); border-color: rgba(46, 224, 110, 0.30); color: #2ee06e; }
body.pubv2.pv2home .pv2p-cell:nth-child(3) .pv2p-chead .pv2p-icon { background: rgba(155, 92, 246, 0.12); border-color: rgba(155, 92, 246, 0.34); color: #b98cf9; }
body.pubv2.pv2home .pv2p-cell:nth-child(4) .pv2p-chead .pv2p-icon { background: rgba(242, 197, 34, 0.10); border-color: rgba(242, 197, 34, 0.32); color: #f2c522; }

/* the welcome head's <strong>/<em> brand emphasis. The tags are frozen
   content; the browser's 700 default out-shouts a 400 heading. 600 is the
   system's one emphasis weight. */
body.pubv2.pv2home .pv2p-shead strong,
body.pubv2.pv2home .pv2p-shead .items { font-weight: 600; }

/* ------------------------------------------------------------
   6.3  THE CONTROL-PANEL FIGURE — "the computer on the right needs to
        show the updated control panel". The template now serves the v2
        monitor render (1218x734, 2x) on the preview; at .pv2p-media's
        stock clamp(250px, 40%, 420px) the panel ON the screen is an
        unreadable smudge. Give the figure an honest half of the split
        (native 1x is 609px) and centre the copy against it.
   ------------------------------------------------------------ */
@media only screen and (min-width: 760px) {
	body.pubv2.pv2home section.alternate .pv2p-media {
		grid-template-columns: minmax(0, 1fr) clamp(300px, 47%, 610px);
		align-items: center;
	}
}

/* ------------------------------------------------------------
   6.4  THE FEATURE CHECKLIST BAND — 24 items that rendered as one flat
        grey run of 6x11px ticks. Three columns become three panels with
        an accent inset edge (gold / green / blue) and the pw-feat
        gold-circle tick, tinted per column.

        NOTE: in the v2 branch the row does NOT carry .hidden-xs (the
        template gates it out), so Part 5.3's `.row.text-left.hidden-xs`
        rules — written for the 288 game pages — are inert here by
        selector. This section owns the band on the home page alone.

        Specificity: Part 3's tick repair is (0,3,2)+::before via
        `body.pubv2 ul.list-icon.check-circle > li:before`; every rule
        below carries .pv2home + .callout.dark and out-specifies it, and
        re-declares every property the repair sets.
   ------------------------------------------------------------ */
body.pubv2.pv2home .callout.dark .row.text-left > [class*="col-"] > ul.list-icon {
	background: rgba(0, 0, 0, 0.26);
	border: 1px solid var(--pv2-border);
	border-radius: 8px;
	box-shadow: inset 3px 0 0 rgba(242, 197, 34, 0.85);
	padding: 15px 16px 15px 18px;
	margin: 0;
}
body.pubv2.pv2home .callout.dark .row.text-left > [class*="col-"]:nth-child(2) > ul.list-icon { box-shadow: inset 3px 0 0 rgba(46, 224, 110, 0.80); }
body.pubv2.pv2home .callout.dark .row.text-left > [class*="col-"]:nth-child(3) > ul.list-icon { box-shadow: inset 3px 0 0 rgba(88, 166, 255, 0.80); }
body.pubv2.pv2home .callout.dark .row.text-left ul.list-icon > li {
	position: relative;
	margin: 0; padding: 5px 0 5px 29px;
	font-size: 13.5px; line-height: 1.5; color: #c9cfd5;
}
body.pubv2.pv2home .callout.dark .row.text-left ul.list-icon > li:before {
	content: '\2713';
	position: absolute; left: 0; top: 7px;
	width: 18px; height: 18px; margin: 0; border: 0;
	border-radius: 50%; transform: none;
	background: #d9a615; color: #17191d;
	font-family: var(--pv2p-font);
	font-size: 11px; font-weight: 800; line-height: 18px; text-align: center;
}
body.pubv2.pv2home .callout.dark .row.text-left > [class*="col-"]:nth-child(2) ul.list-icon > li:before { background: #2ec962; }
body.pubv2.pv2home .callout.dark .row.text-left > [class*="col-"]:nth-child(3) ul.list-icon > li:before { background: #4f9be8; }
/* stacked panels below 768 (bootstrap's col-sm-4 stops applying there) */
@media only screen and (max-width: 767px) {
	body.pubv2.pv2home .callout.dark .row.text-left > [class*="col-"] { margin-bottom: 12px; }
	body.pubv2.pv2home .callout.dark .row.text-left > [class*="col-"]:last-child { margin-bottom: 0; }
}

/* ------------------------------------------------------------
   6.5  THE REVIEWS BAND — .pv2h-reviews (the gated class index.tpl puts
        on the testimonials section). The band takes the panel rung with
        a gold bloom rising from its BOTTOM edge, so the section hands off
        into the footer's own gold top hairline instead of two identical
        near-blacks butting against each other — that seam is the
        "transition looks really weird" note. The quotes become raised
        review cards: serif gold quote mark, the reviewer's name white
        over a hairline, body at reading size.
   ------------------------------------------------------------ */
body.pubv2.pv2home .pv2h-reviews {
	background-color: var(--pv2-panel);
	background-image:
		radial-gradient(760px 300px at 50% 104%, rgba(234, 168, 36, 0.10), rgba(234, 168, 36, 0) 72%),
		repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.012) 0 1px, rgba(255, 255, 255, 0) 1px 8px);
	box-shadow: inset 0 1px 0 var(--pv2-line), inset 0 -1px 0 var(--pv2-line);
}
body.pubv2.pv2home .pv2h-reviews .pv2p-card.pv2p-quote {
	position: relative;
	margin: 0;
	background: var(--pv2-raised);
	border: 1px solid var(--pv2-border);
	border-radius: var(--pv2-radius-lg);
	padding: 16px 20px 14px 56px;
	font-size: 14px; line-height: 1.6; color: var(--pv2-text-soft);
}
body.pubv2.pv2home .pv2h-reviews .pv2p-card.pv2p-quote:before {
	content: '\201C';
	position: absolute; left: 17px; top: 7px;
	font-family: Georgia, 'Times New Roman', serif;
	font-size: 46px; line-height: 1;
	color: var(--pv2-gold); opacity: 0.85;
}
body.pubv2.pv2home .pv2h-reviews .pv2p-quote cite {
	display: block; margin: 12px 0 0; padding-top: 10px;
	border-top: 1px solid var(--pv2-line);
	font-style: normal; font-size: 12px; color: var(--pv2-text-muted);
}
body.pubv2.pv2home .pv2h-reviews .pv2p-quote cite strong { color: #fff; font-weight: 600; }


/* ---------- the world map is a proof point, not a feature ----------
   Ryan: "this is a bit too large." At the full 1560 rail the SVG's 1000x390
   viewBox makes it 608px tall, so the map plus its heading and the eight city
   links fill an entire laptop viewport - you scroll a screen of mostly ocean
   to reach the thing the section is actually selling, which is the city list.
   Capping the WIDTH rather than squashing the height keeps the projection
   honest; the pins stay on their cities. ~1180 puts it near 460px tall, still
   comfortably readable at a glance. */
body.pubv2 .pv2p-worldmap-wrap,
body.pubv2 .world-map:has(> svg.pv2p-worldmap) {
	max-width: 1180px;
	margin-left: auto;
	margin-right: auto;
}
@media (max-width: 1199px) {
	body.pubv2 .pv2p-worldmap-wrap,
	body.pubv2 .world-map:has(> svg.pv2p-worldmap) { max-width: 100%; }
}
