/*
Theme Name: WanTok New
Theme URI: https://wantok.to
Author: WanTok
Author URI: https://wantok.to
Description: A modern, responsive WordPress theme for WanTok — Simple. Reliable. Affordable. Built for the Kingdom of Tonga's leading ICT provider.
Version: 2.0.0
Requires at least: 6.0
Tested up to: 6.7
Requires PHP: 7.4
License: Proprietary
Text Domain: wantok-new
Tags: custom-logo, custom-menu, featured-images, responsive-layout, translation-ready
*/

/* =====================================================================
   WanTok Refresh — theme stylesheet
   Brand: the original ocean cyan/blue/purple gradient on a white canvas —
   restored per request after a brief tapa-cream/brown experiment. Tongan
   ngatu (tapa cloth) design is still present, but expressed as SHAPE
   rather than a colour swap: a zigzag "kupesi" stamp mark before every
   section eyebrow, a real recoloured kupesi motif band under the hero,
   and sharp/graphic radii (rather than soft rounded-app defaults) —
   all rendered in the company's own cyan/blue/purple, not earth tones.
   Type: Space Grotesk (display) + Inter (body).
   ===================================================================== */

:root {
	/* Brand — restored to the original cyan/blue/purple/navy set. */
	--wt-cyan: #13b6e6;
	--wt-blue: #1170d6;
	--wt-blue-deep: #0a4fa6;
	--wt-purple: #7a39bb;
	--wt-purple-deep: #5a23a0;
	--wt-navy: #0c1330;
	--wt-navy-2: #131c44;

	/* Surfaces — back to white/cool blue-white, not tapa-cream. */
	--wt-bg: #ffffff;
	--wt-surface: #f5f8fc;
	--wt-surface-2: #eef3fa;
	--wt-border: #dde5f0;

	/* Text */
	--wt-ink: #131a2b;
	--wt-ink-2: #45506a;
	--wt-ink-3: #6b7690;
	--wt-on-dark: #f3f6fc;
	--wt-on-dark-2: #aab6d4;

	/* Gradient — cyan → blue → purple, the company's own colours. */
	--wt-grad: linear-gradient(120deg, var(--wt-cyan) 0%, var(--wt-blue) 45%, var(--wt-purple) 100%);
	--wt-grad-soft: linear-gradient(120deg, rgba(19,182,230,0.12), rgba(122,57,187,0.12));
	/* Flattened back to solid navy (16 July, later the same day) — Soana
	   asked for the page banner ("header") and closing CTA band ("footer")
	   to both go back to plain navy instead of the gradient added earlier
	   today. Kept as its own variable (rather than replacing every
	   --wt-navy-grad reference with --wt-navy directly) so both spots stay
	   wired to one source of truth if a gradient's ever wanted again. */
	--wt-navy-grad: var(--wt-navy);

	/* Type */
	--wt-font-display: 'Space Grotesk', ui-sans-serif, system-ui, sans-serif;
	--wt-font-body: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

	/* Layout — smaller radii than the old soft-app defaults, closer to a
	   printed panel than a rounded card. Kept from the tapa pass — this is
	   shape language, not colour, and pairs with the kupesi motif below. */
	--wt-maxw: 1180px;
	--wt-radius: 6px;
	--wt-radius-sm: 4px;
	--wt-shadow: 0 1px 2px rgba(12,19,48,0.06), 0 8px 24px rgba(12,19,48,0.07);
	--wt-shadow-lg: 0 18px 48px rgba(12,19,48,0.14);

	--wt-space: 1rem;
}

/* ---------- Reset-ish ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* overflow-x:hidden here (not on body, to avoid any risk of interfering
   with the sticky header below) clips a real layout bug: the "Services"
   nav dropdown (.mega-menu) and the partner-logo marquee are both wider
   than the viewport by design (a hidden-until-hover dropdown, and a
   duplicated-content infinite-scroll strip) but nothing was clipping
   that width at the document level, so the whole page's scrollable area
   silently extended past the viewport edge — invisible during normal
   browsing, but it showed up as blank canvas on the right of a full-page
   screenshot, and could cause a stray horizontal scrollbar. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: hidden; }
@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
}

/* ---------- Reveal-on-scroll ----------
   15 July: theme.js already had a complete IntersectionObserver-driven
   reveal system (targets [data-reveal], .service-card, .plan-card,
   .news-card, .why-list li — see "Reveal-on-scroll" in theme.js) — it
   adds .reveal-init then .is-visible, with a reduced-motion bypass and a
   2.5s safety net so nothing gets stuck invisible if the observer never
   fires. What was missing was this half: the actual hidden/visible look.
   Kept deliberately restrained — a short rise + fade, not a bounce or a
   slide from the side, and unobserved after the first reveal so it's a
   one-time entrance, not something that replays every time you scroll
   past a section. data-reveal was also added to each section's header
   block and the CTA band content, so headers ease in as one block just
   before their cards cascade in individually right after. */
.reveal-init {
	opacity: 0; transform: translateY(22px);
	transition: opacity 0.7s ease, transform 0.7s ease;
}
.reveal-init.is-visible { opacity: 1; transform: none; }
body {
	margin: 0;
	font-family: var(--wt-font-body);
	font-size: 17px;
	line-height: 1.65;
	color: var(--wt-ink);
	background: var(--wt-bg);
	-webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
a { color: var(--wt-blue); text-decoration: none; }
a:hover { color: var(--wt-purple); }
/* Scoped to "body.wp-theme-wantok-new" (a class WordPress adds to every
   page automatically for the active theme) rather than a bare tag
   selector — Elementor's global "Kit" stylesheet ships its own
   `.elementor-kit-1 h1`/`h2`/`h3`/`h4`/`h5`/`h6` rules (font-family:
   Nexa, and for h1 specifically a flat font-size:40px/font-weight:900
   too), and a plain `h1, h2, h3, h4` selector has lower CSS specificity
   than that, so it was silently losing — every heading site-wide was
   quietly rendering in the wrong typeface (discovered 15 July while
   chasing why the hero title wouldn't get bigger no matter what value
   was set — it wasn't a sizing bug, the value was being overridden
   outright). This selector is one specificity tier higher so it wins
   regardless of stylesheet load order. */
body.wp-theme-wantok-new h1, body.wp-theme-wantok-new h2, body.wp-theme-wantok-new h3, body.wp-theme-wantok-new h4 { font-family: var(--wt-font-display); line-height: 1.15; color: var(--wt-ink); margin: 0 0 0.5em; font-weight: 700; letter-spacing: -0.01em; }
p { margin: 0 0 1rem; }
ul { margin: 0; padding: 0; }
:focus-visible { outline: 3px solid var(--wt-blue); outline-offset: 2px; border-radius: 4px; }

/* ---------- Accessibility helpers ---------- */
.screen-reader-text {
	position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
	overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link {
	position: absolute; left: -9999px; top: 0; z-index: 9999;
	background: var(--wt-navy); color: #fff; padding: 12px 18px; border-radius: 0 0 8px 0;
	font-weight: 600;
}
.skip-link:focus { left: 0; color: #fff; }

/* ---------- Layout ---------- */
.container { width: 100%; max-width: var(--wt-maxw); margin: 0 auto; padding: 0 22px; }
.content-area { padding-top: 48px; padding-bottom: 64px; }

/* ---------- Buttons ---------- */
.btn {
	display: inline-flex; align-items: center; justify-content: center; gap: 0.5em;
	font-family: var(--wt-font-display); font-weight: 600; font-size: 0.98rem;
	padding: 0.72em 1.4em; border-radius: 8px; border: 2px solid transparent;
	cursor: pointer; transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease, color 0.2s ease;
	text-align: center; line-height: 1.1;
}
.btn:hover { transform: translateY(-1px); }
/* Round 27 (14 July) — the cyan/blue/purple gradient dropped in favour of
   plain navy, so nothing on the homepage still uses the old brand
   gradient (matches the monochrome-navy language the rest of the page
   settled on this session). */
.btn-primary { background: var(--wt-navy); color: #fff; box-shadow: 0 6px 18px rgba(12,19,48,0.35); }
.btn-primary:hover { color: #fff; box-shadow: 0 10px 26px rgba(12,19,48,0.4); }
.btn-outline { background: #fff; color: var(--wt-blue-deep); border-color: var(--wt-border); }
.btn-outline:hover { color: var(--wt-purple); border-color: var(--wt-purple); }
.btn-ghost { background: transparent; color: var(--wt-blue-deep); border-color: transparent; }
.btn-ghost:hover { color: var(--wt-purple); }
.btn-light { background: #fff; color: var(--wt-navy); }
.btn-light:hover { color: var(--wt-purple-deep); }
.btn-ghost-light { background: transparent; color: #fff; border-color: rgba(255,255,255,0.5); }
.btn-ghost-light:hover { color: #fff; border-color: #fff; }
/* Added 16 July for inner-page closing CTAs (starting with Support), per a
   reference design the client liked — a rounded pill filled with the brand
   gradient, for one primary action to stand out against a navy CTA band.
   This doesn't reverse Round 27's move away from the gradient on the
   homepage's own buttons above; it's a deliberate, separate accent used
   sparingly on inner pages only. */
.btn-gradient { background: var(--wt-grad); color: #fff; border-radius: 999px; box-shadow: 0 10px 26px rgba(90,35,160,0.35); }
.btn-gradient:hover { color: #fff; filter: brightness(1.08); box-shadow: 0 14px 32px rgba(90,35,160,0.42); }

/* ---------- Utility bar ---------- */
.utility-bar { background: var(--wt-navy); color: var(--wt-on-dark); font-size: 0.86rem; }
.utility-inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: 42px; flex-wrap: wrap; }
.utility-contact { display: flex; gap: 1.25rem; list-style: none; flex-wrap: wrap; }
.utility-contact a { color: var(--wt-on-dark); display: inline-flex; align-items: center; gap: 0.4em; }
.utility-contact a:hover { color: var(--wt-cyan); }
.util-ico { opacity: 0.85; }
/* 30 Aug 2026 — "Network Dock" redesign: phone and email must always show
   together (Soana: "very important to me to have both"), joined by this
   middot rather than two separate list items. Explicit colour needed —
   without it the separator inherits body's near-black ink colour and
   disappears against the navy bar. */
.util-sep { color: var(--wt-on-dark-2); }
/* My WanTok + the social icons share this end of the utility bar (see
   header.php). Briefly moved into a floating nav card during the 30 Aug
   "Network Dock" round, then moved straight back here the same day when
   the header reverted to the flush "Network Rail" bar — see
   wantok_header_network_rail_revert_30aug.md. */
.utility-actions { display: flex; align-items: center; gap: 1rem; }
.util-portal {
	color: #fff; font-weight: 600; background: rgba(255,255,255,0.08);
	padding: 5px 14px; border-radius: 999px; border: 1px solid rgba(255,255,255,0.18);
}
.util-portal:hover { color: #fff; background: rgba(19,182,230,0.25); border-color: var(--wt-cyan); }
.utility-social { display: flex; gap: 0.4rem; list-style: none; }
.utility-social a {
	display: inline-grid; place-items: center; width: 26px; height: 26px;
	border-radius: 6px; background: rgba(255,255,255,0.08); color: var(--wt-on-dark);
}
/* Lighter navy rather than flat var(--wt-navy) — the bar itself is
   already navy, so a same-navy hover fill would be invisible. */
.utility-social a:hover { background: #3d4f96; color: #fff; }
/* 28 August 2026 — real Facebook/X/LinkedIn/Instagram brand marks
   (wantok_social_icon_svg() in functions.php) replacing the old plain
   "f"/"X"/"in"/"ig" text-letter placeholders. The <svg> just inherits
   .utility-social a's own `color` via fill="currentColor", so the
   existing default/hover colours above still apply with no other change
   needed. Sized a little smaller than the 26px box so the mark doesn't
   crowd its edges. */
.utility-social .social-ico { width: 14px; height: 14px; display: block; }

/* ---------- Header ----------
   Navy by default everywhere — same var(--wt-navy) as .utility-bar right
   above it, and no border between them, so the two read as one seamless
   navy band instead of a dark strip stacked on a separate white bar
   (this was a deliberate change on 8 July: the previous solid-white
   header looked like two different-coloured bars stacked together). The
   one exception is .hero-overlay on the homepage, which starts fully
   transparent over the hero photo and only becomes this same navy once
   .is-scrolled fires (see theme.js) — text/logo colours don't need to
   change between the transparent and solid states, since both sit on a
   dark background either way; only the background itself toggles. */
.site-header {
	position: sticky; top: 0; z-index: 100; background: var(--wt-navy);
	transition: background 0.25s ease, box-shadow 0.25s ease;
}
.site-header.is-scrolled { box-shadow: var(--wt-shadow-lg); }
.header-inner { display: flex; align-items: center; gap: 1.5rem; min-height: 72px; }
.site-branding { flex: 0 0 auto; }
.brand-wordmark { display: inline-flex; align-items: center; }
.custom-logo { max-height: 46px; width: auto; }
.custom-logo-link { display: inline-flex; align-items: center; }

/* Real logo (assets/images/wantok-logo-{white,navy}.png) — updated 15 July
   to the fuller lockup with the "Simple. Reliable. Affordable." tagline
   baked in under the wordmark, replacing the wordmark-only crop. The
   header is navy in every state now, so the white mark is simply the
   default — the navy copy is kept only in case a future light-background
   header variant is wanted, but nothing currently shows it. Height bumped
   34px→46px from the wordmark-only version so the tagline line stays
   legible at nav-bar scale. */
.wantok-logo-swap { position: relative; display: inline-block; height: 46px; }
.wantok-logo-swap .wantok-logo { display: block; height: 46px; width: auto; transition: opacity 0.25s ease; }
.wantok-logo-swap .wantok-logo-dark { position: absolute; inset: 0; opacity: 0; }
.wantok-logo-swap .wantok-logo-light { opacity: 1; }
/* Footer has real vertical room, so its copy of the same lockup can run
   larger — the tagline should be comfortably readable here, not just
   present. */
.footer-logo .wantok-logo-swap { height: 64px; }
.footer-logo .wantok-logo-swap .wantok-logo { height: 64px; }

/* Homepage-only: the header starts transparent, floating directly over
   the hero photo (the hero is pulled up underneath it via a matching
   negative margin in front-page.php's "front-main hero-overlay-page"
   wrapper) instead of sitting above it as a separate bar. The threshold
   for .is-scrolled is set in theme.js based on the hero's own height,
   not the default 8px scroll-shadow trigger, so it stays transparent
   through the hero and only goes navy once scrolled past it. Inner
   pages never get .hero-overlay, so their header is navy from the start. */
.site-header.hero-overlay { background: transparent; }
.site-header.hero-overlay.is-scrolled { background: var(--wt-navy); box-shadow: var(--wt-shadow-lg); }
/* The utility strip now lives inside #masthead (see header.php, "Network
   Dock" redesign), so it inherits the same transparent-over-the-hero /
   solid-once-scrolled behaviour as the nav card below purely by being a
   descendant of the same two classes — no separate JS toggle needed. */
.site-header.hero-overlay .utility-bar { background: transparent; }
.site-header.hero-overlay.is-scrolled .utility-bar { background: var(--wt-navy); }

/* ---------- Nav row ("Network Rail") ----------
   30 Aug 2026 — briefly rebuilt as a floating rounded "Network Dock"
   card (own padding/radius/shadow, translucent-then-blurred over the
   hero) and reverted the same day back to this: the branding + primary
   nav sit in a plain, full-width bar flush with the header's own edges —
   same navy band as the utility bar directly above it, no card, no
   shadow, no blur. Soana's call after comparing both built live: Network
   Rail's own signature (the line-and-node nav below) reads more clearly
   without competing for attention with a floating card shape, and it's
   one less browser-support caveat (no @supports/backdrop-filter path to
   maintain). See wantok_header_network_rail_revert_30aug.md. */
.nav-row { background: var(--wt-navy); }
.site-header.hero-overlay .nav-row { background: transparent; }
.site-header.hero-overlay.is-scrolled .nav-row { background: var(--wt-navy); }
.header-inner { display: flex; align-items: center; gap: 1.5rem; min-height: 72px; }

.main-navigation {
	margin-left: auto;
	/* Round 8 (27 Aug) — positioning ancestor for .mega-menu (see that
	   rule and .nav-menu li.menu-item-has-children below): anchoring the
	   dropdown here instead of to the "Services and Plans" <li> itself
	   keeps its right edge aligned with the header container's stable
	   inner-right edge as the fluid header narrows, instead of drifting
	   with that one nav item's on-screen position. */
	position: relative;
}
/* "Network Dock" signature: a faint rail line runs behind the nav items
   with a small node under each link, lighting up cyan on hover and
   pulsing gently on the current page — replaces the old plain underline
   treatment on .current-menu-item. Pure CSS on the existing WP-rendered
   markup (wantok_render_primary_nav()/wantok_fallback_menu() in
   functions.php, untouched) — no new markup needed. */
.nav-menu { display: flex; align-items: center; gap: 0.35rem; list-style: none; position: relative; }
.nav-menu::before {
	content: ""; position: absolute; left: 4px; right: 4px; top: 50%;
	height: 1px; background: rgba(255,255,255,0.16); z-index: 0;
}
.nav-menu li { position: relative; z-index: 1; }
.nav-menu a {
	display: inline-block; padding: 0.55rem 0.85rem 0.95rem; border-radius: 8px;
	font-weight: 500; color: var(--wt-on-dark); font-size: 0.98rem; position: relative;
	/* Keeps every item (notably "Services and Plans" and "About Us") on
	   one line as the card's own row shrinks it — without this, flexbox
	   wraps the longer labels onto two lines well before the row actually
	   runs out of space, taller-item first, which is what widened the
	   861-1080px squeeze below. Desktop-only selector (.nav-menu, not the
	   mobile flyout's own — this is overridden back to normal wrapping
	   inside the 860px flyout, where each item gets its own full-width
	   row anyway). */
	white-space: nowrap;
}
.nav-menu a::after {
	content: ""; position: absolute; left: 50%; bottom: 7px; transform: translateX(-50%);
	width: 5px; height: 5px; border-radius: 50%; background: rgba(255,255,255,0.35);
	transition: background 0.2s ease, box-shadow 0.2s ease;
}
.nav-menu a:hover, .nav-menu .current-menu-item > a { color: #fff; background: rgba(255,255,255,0.14); }
.nav-menu a:hover::after, .nav-menu .current-menu-item > a::after {
	background: var(--wt-cyan); box-shadow: 0 0 8px 2px rgba(19,182,230,0.55);
}
.nav-menu .current-menu-item > a::after { animation: wt-rail-pulse 2.4s ease-in-out infinite; }
@keyframes wt-rail-pulse {
	0%, 100% { box-shadow: 0 0 6px 1px rgba(19,182,230,0.5); }
	50% { box-shadow: 0 0 12px 4px rgba(19,182,230,0.8); }
}
@media (prefers-reduced-motion: reduce) {
	.nav-menu .current-menu-item > a::after { animation: none; }
}

/* 30 Aug 2026 — the 861-1080px compaction band that used to live here
   (tightening .nav-card/.nav-menu/.nav-card-portal spacing) only existed
   because My WanTok briefly shared this row with the primary nav during
   the "Network Dock" round, pushing "Services and Plans" past the card's
   edge starting ~900px wide. Now that My WanTok has moved back to the
   utility bar (see wantok_header_network_rail_revert_30aug.md), the nav
   row only ever holds the logo + primary nav, which fit within the same
   861px single-row-nav floor the Round 8/9 mega-menu notes above rely on
   without needing any extra squeeze — re-verified with the same
   Playwright width sweep, 861-1180px, zero wrap and zero overflow with
   this band removed entirely. */

/* Dropdowns */
.nav-menu li { position: relative; }
/* Round 8 (27 Aug) — the mega-menu's own <li> ("Services and Plans")
   would otherwise be the closest positioned ancestor for its
   position:absolute .mega-menu child (per the rule just above), and its
   on-screen position shifts as the fluid header narrows, which is what
   pushed the dropdown off the left edge of the viewport at mid-range
   widths (see Round 7's 2x2 fallback comment, now removed below). Taking
   this one <li> back out of the positioning chain makes .main-navigation
   the containing block instead (see its "position: relative" above) —
   a stable anchor whose right edge tracks the header container's own
   right-inner-edge, not one nav item's position within it. Scoped to
   .menu-item-has-children specifically (only "Services and Plans" ever
   carries this class — confirmed in functions.php, both
   wantok_fallback_menu() and wantok_render_primary_nav() apply it to no
   other item) so no other nav <li> is affected. */
.nav-menu li.menu-item-has-children { position: static; }
.nav-menu .sub-menu {
	position: absolute; top: 100%; left: 0; min-width: 230px; background: var(--wt-bg);
	border: 1px solid var(--wt-border); border-radius: var(--wt-radius-sm);
	box-shadow: var(--wt-shadow-lg); padding: 8px; list-style: none;
	opacity: 0; visibility: hidden; transform: translateY(6px);
	transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s; z-index: 50;
}
.nav-menu li:hover > .sub-menu,
.nav-menu li:focus-within > .sub-menu,
.nav-menu li.submenu-open > .sub-menu { opacity: 1; visibility: visible; transform: translateY(0); }
.nav-menu .sub-menu a { display: block; padding: 0.5rem 0.7rem; font-size: 0.93rem; }
.submenu-toggle { display: none; }

/* Mega menu (Services and Plans) — grouped columns instead of one long
   list. 26 August 2026 redesign, round 4: rounds 1-3 used a flat divider
   line between columns (grey, then cyan/purple-tinted) to show grouping.
   Soana said it read as "too much" and asked for the same soft cyan/
   purple "glow" used elsewhere on the site instead (.services-showcase-
   card.glow-cyan/-purple, .plans .plan-card.glow-cyan/-purple,
   .feature-mosaic-tile.glow-cyan/-purple — see those for the same
   tinted-border + blurred-halo pattern) plus a rounder outer box. So:
   dropped the divider lines and the top gradient bar entirely, each
   group is now its own soft rounded tile with a coloured glow (or solid
   navy for "Coming Soon"), sitting in a real grid gap instead of sharing
   an edge — simpler markup-to-CSS mapping too, no more bleed/negative-
   margin hack for the "Coming Soon" panel.
   Round 5 (same day): the 4-across grid meant narrow columns, so short
   titles like "Consumer Broadband" wrapped onto two cramped lines.
   Shown three ways to fix it (wider row, 2x2 grid, single stacked
   column) — Soana picked the single stacked column: groups now stack
   top to bottom, full width, so every title always gets one line.
   Narrower box, taller drop — glow bumped to the upper end of the
   sitewide range since each tile is now a full-width surface.
   Round 7 (27 Aug) — Soana's boss asked to go back to a horizontal row
   instead. Round 5's "4-across" problem was equal-width (1fr) columns
   forcing every group into the same narrow box regardless of how long
   its content actually was — this time each column gets its own
   explicit width, sized off the REAL rendered text (measured via a
   Playwright probe against the live .mega-col-title/li a/.mega-item-
   disabled elements, not guessed or left to grid's own minmax/auto
   sizing — see the "gotcha" note this file's history already flagged
   about grid auto-sizing not reliably giving a column its true
   unwrapped width in a constrained container). Longest unwrapped
   content per group at this font: Connectivity 196px ("Starlink
   Satellite Service"), ICT 269px ("Utility Management System (UMS)"),
   Hardware 98px ("Anker Solix"), Coming Soon 125px text + ~58px for
   the "Soon" badge. Coming Soon is deliberately given the narrowest
   column of the four (180px, vs. 240/310/150 for the others) per
   Soana's "make Coming Soon smaller" ask — it's also given tighter
   internal padding below (.mega-col--panel) so it reads as visibly
   more compact, not just numerically narrower. */
.nav-menu .mega-menu {
	/* Anchored to the item's RIGHT edge, not left — "Services and Plans"
	   sits mid-row with News/Support still to its right, so a left:0
	   anchor ran the menu off the right edge of the viewport at ordinary
	   desktop widths.
	   Round 9 (27 Aug) — Round 7's fixed 940px box width didn't actually
	   match its own grid: 4 fixed columns (240+310+150+180=880) + 3 gaps
	   (16px each, 48) + the box's own left/right padding (22px each, 44)
	   adds up to 972px of required content width, 32px more than the
	   940px box declared — with no overflow:hidden on the box, that 32px
	   of grid content (the rightmost column) rendered outside the
	   rounded white card instead of inside it. Switching width to auto
	   (shrink-to-fit, capped by max-width so it still can't run off a
	   narrow viewport) and each column to minmax(min-content, cap) makes
	   the box's own size and its grid's actual content size the same
	   calculation, so the two can no longer disagree — the box is always
	   exactly as wide as its columns need, up to each column's cap, and
	   never wider than the viewport allows. Below the point where even
	   min-content no longer fits (deep into the 861px mobile-flyout
	   boundary), the longest item in a column wraps onto a second line
	   same as any normal text box — it does not spill outside the card.
	   Width is a definite min(972px, 94vw) rather than auto: an auto
	   width on an absolutely-positioned box is resolved via shrink-to-
	   fit against its CONTAINING block's own width (here, .main-
	   navigation — see Round 8's note on why that's the positioning
	   ancestor) — and .main-navigation is itself only as wide as its nav
	   links need to be, not the leftover header space, so shrink-to-fit
	   clamped the menu down to that much narrower figure on every
	   viewport, wrapping every column's text unnecessarily. A definite
	   width sidesteps that: 972px is the exact sum of the 4 columns'
	   caps (240+310+150+180=880) + 3 gaps (16px, 48) + the box's own
	   left/right padding (22px, 44) — so at full size every column gets
	   its full cap with nothing left over and nothing overflowing, and
	   the 94vw fallback still shrinks the box (and, via minmax, its
	   columns) safely on narrower screens without changing the sizing
	   quirk above. */
	position: absolute; top: 100%; right: 0; left: auto; width: min(972px, 94vw);
	background: var(--wt-bg); border: none; border-radius: 22px;
	box-shadow: var(--wt-shadow-lg); padding: 22px; z-index: 50;
	display: grid;
	grid-template-columns: minmax(min-content, 240px) minmax(min-content, 310px) minmax(min-content, 150px) minmax(min-content, 180px);
	gap: 16px;
	/* Round 9 — was align-items: start, which let each column shrink to
	   its own content height. With Connectivity/Hardware holding 2 items
	   and ICT holding 4, that produced 4 visibly different-height tiles
	   (a jagged bottom edge) instead of 4 matching "boxes". Stretch makes
	   every column fill the row's full height — set by whichever column
	   is tallest — so all four read as the same shape. */
	align-items: stretch;
	opacity: 0; visibility: hidden; transform: translateY(6px);
	transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
}
.nav-menu li:hover > .mega-menu,
.nav-menu li:focus-within > .mega-menu,
.nav-menu li.submenu-open > .mega-menu { opacity: 1; visibility: visible; transform: translateY(0); }
.mega-col {
	padding: 18px 16px; border-radius: 16px; background: var(--wt-surface);
	border: 1px solid transparent;
}
.mega-col--cyan {
	border-color: color-mix(in srgb, var(--wt-cyan) 45%, transparent);
	box-shadow: 0 0 26px color-mix(in srgb, var(--wt-cyan) 20%, transparent);
}
.mega-col--purple {
	border-color: color-mix(in srgb, var(--wt-purple) 45%, transparent);
	box-shadow: 0 0 26px color-mix(in srgb, var(--wt-purple) 20%, transparent);
}
.mega-col-title {
	display: block; font-size: 0.74rem; font-weight: 700; letter-spacing: 0.05em;
	text-transform: uppercase; color: var(--wt-purple); margin-bottom: 0.85rem;
}
.mega-col--cyan .mega-col-title { color: var(--wt-blue-deep); }
.mega-col--purple .mega-col-title { color: var(--wt-purple-deep); }
.mega-col ul { list-style: none; display: flex; flex-direction: column; gap: 0.5rem; }
.mega-col li a {
	display: block; padding: 0.4rem 0.5rem; margin: 0 -0.5rem; border-radius: 8px;
	font-size: 0.92rem; font-weight: 500; color: var(--wt-ink); line-height: 1.4;
}
.mega-col li a:hover { color: var(--wt-blue); background: rgba(17,112,214,0.08); }
.mega-col li a span[aria-hidden] { opacity: 0.6; font-size: 0.85em; margin-left: 1px; }
.mega-item-disabled {
	display: inline-block; padding: 0.4rem 0.5rem; margin: 0 -0.5rem;
	font-size: 0.92rem; font-weight: 500; color: var(--wt-ink-3); line-height: 1.4; cursor: default;
}
.mega-badge {
	display: inline-block; margin-left: 0.4rem; padding: 2px 8px; border-radius: 999px;
	font-size: 0.66rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase;
	color: #fff; background: var(--wt-purple); vertical-align: 1px;
}
/* "Coming Soon" — same rounded-tile treatment as the glow columns, just
   filled solid navy instead of tinted/glowing, so it still reads as
   distinct ("not live yet") without needing its own bleed/edge hack. */
/* Round 7 (27 Aug) — tighter padding/type scale than the other 3 tiles
   (18px 16px / 0.74rem title / 0.92rem items elsewhere) so "Coming Soon"
   reads as visibly smaller/lower-priority, not just narrower — Soana's
   boss asked for it to be smaller than the rest of the groups, and a
   narrower column alone (see .nav-menu .mega-menu's grid-template-
   columns) wasn't going to carry that on its own once the badge text is
   accounted for. */
.mega-col--panel {
	background: var(--wt-navy); border-color: transparent; padding: 14px 14px;
}
.mega-col--panel .mega-col-title { color: var(--wt-on-dark); font-size: 0.68rem; margin-bottom: 0.6rem; }
.mega-col--panel .mega-col-desc {
	display: block; font-size: 0.72rem; color: var(--wt-on-dark-2); margin: -0.35rem 0 0.7rem;
}
.mega-col--panel .mega-item-disabled { color: var(--wt-on-dark-2); font-size: 0.85rem; padding: 0.3rem 0.4rem; margin: 0 -0.4rem; }
.mega-col--panel .mega-badge { background: var(--wt-cyan); color: var(--wt-navy); font-size: 0.62rem; padding: 1px 7px; }
.mega-col--panel ul { gap: 0.35rem; }
/* "View all Services and Plans" — first tried as a quiet text row under
   the last tile; Soana asked for it at the top instead, as an actual
   focal point. Now the first thing the dropdown shows: a bold gradient
   banner using the site's existing .btn-gradient treatment (the cyan-
   blue-purple gradient reserved sitewide as "one primary action to
   stand out", see that rule's own note above) rather than a new pattern
   invented just for this menu. Scoped as ".mega-menu .mega-menu-cta"
   (not the bare class) so this out-specifies the site's ".nav-menu a"
   rule, which styles top-level nav links for the navy bar (light text)
   — without the extra ".mega-menu" ancestor, that rule would win here. */
.mega-menu .mega-menu-cta {
	/* Round 7 (27 Aug) — the menu's 4 groups now sit side by side in
	   explicit grid columns instead of stacking full-width, so this
	   banner (still meant to run the full width of the dropdown, above
	   every group) needs an explicit span across all of them — without
	   it, grid auto-placement would drop it into column 1 alone. */
	grid-column: 1 / -1;
	display: flex; align-items: center; justify-content: center; gap: 8px;
	margin-bottom: 14px; padding: 14px 18px; border-radius: 16px;
	background: var(--wt-grad); box-shadow: 0 10px 26px rgba(90,35,160,0.35);
	font-family: var(--wt-font-display); font-size: 1rem; font-weight: 700;
	color: #fff; text-decoration: none;
	transition: filter 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.mega-menu .mega-menu-cta:hover {
	/* Redeclares background (not just filter) -- ".nav-menu a:hover" (the
	   navy-bar link hover state) includes the element type "a" in its
	   selector, which out-specifies the *non-hover* ".mega-menu .mega-
	   menu-cta" rule's background on hover even though this hover rule
	   wins everywhere else, silently replacing the gradient with the
	   nav bar's translucent-white hover fill. Restating it here keeps
	   the gradient through the hover state instead. */
	color: #fff; filter: brightness(1.08); box-shadow: 0 14px 32px rgba(90,35,160,0.42);
	background: var(--wt-grad); transform: translateY(-1px);
}
.mega-menu .mega-menu-cta-arrow { transition: transform 0.15s ease; }
.mega-menu .mega-menu-cta:hover .mega-menu-cta-arrow { transform: translateX(3px); }
/* Round 7 (27 Aug) had a 2x2 fallback grid here for the 861-1180px band,
   because the dropdown was anchored to the "Services and Plans" <li>
   itself and that item's on-screen position shifts as the fluid header
   narrows, pushing the full 940px-wide row off the left edge of the
   viewport in that range. Round 8 (27 Aug) fixes this at the root
   instead — see .main-navigation's "position: relative" and
   ".nav-menu li.menu-item-has-children { position: static; }" above —
   by re-anchoring the dropdown to the header's stable inner-right edge
   rather than to one nav item's drifting position. Soana's boss wanted
   one row, not a 2x2 fallback, and re-anchoring keeps the single 4-across
   row viable all the way down to the 861px mobile-flyout handoff, so
   this breakpoint override is no longer needed. Confirmed via the same
   Playwright bounding-box sweep across 861-1440px — zero overflow. */
.submenu-toggle { display: none; }

.menu-toggle {
	display: none; align-items: center; justify-content: center; width: 44px; height: 40px;
	margin-left: auto; background: rgba(255,255,255,0.14); border: 1px solid rgba(255,255,255,0.3);
	border-radius: 10px; cursor: pointer;
}
.menu-toggle-bars { display: grid; gap: 4px; }
.menu-toggle-bars span { display: block; width: 20px; height: 2px; background: #fff; border-radius: 2px; transition: transform 0.2s ease, opacity 0.2s ease; }
.nav-open .menu-toggle-bars span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-open .menu-toggle-bars span:nth-child(2) { opacity: 0; }
.nav-open .menu-toggle-bars span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* ---------- Hero ----------
   Full-bleed cover hero: the carousel images sit as an absolutely-
   positioned background layer spanning the whole section, a dark scrim
   sits above them for text contrast, and the copy is left-aligned on
   top — the header stays a normal solid bar above this section. */
.hero-overlay-page .hero {
	/* Pulls the hero up to close the gap the (now-transparent) header would
	   normally occupy, so the header floats directly on the photo instead
	   of sitting above it. The header itself keeps its normal sticky flow/
	   height — this is purely a visual overlap via z-index (.site-header
	   is z-index:100, well above the hero's own stacking), not an
	   absolute-positioning hack.
	   30 Aug 2026 — the utility bar lives inside #masthead (see header.php),
	   so the header's total floating height is whatever utility-bar +
	   nav-row actually render at. Reverted the same day from the
	   "Network Dock" floating-card treatment back to a flush "Network
	   Rail" bar — the card's own vertical padding is gone, so this value
	   (and the phone-wrap exception below) was re-measured against the
	   real markup, not carried over from the old numbers. The 861-1080px
	   exception that used to exist here is gone too — that band only
	   ever compensated for the floating card, and the flush bar doesn't
	   change height in that range. My WanTok moving back into the utility
	   bar also widened it enough to push the phone/email wrap point from
	   459px up to ~572px (measured, not guessed) — the exception below
	   now triggers there instead. See
	   wantok_header_network_rail_revert_30aug.md. */
	margin-top: -114px;
}
@media (max-width: 572px) {
	.hero-overlay-page .hero { margin-top: -142px; }
}
.hero {
	position: relative; overflow: hidden; display: flex; align-items: center;
	min-height: min(88vh, 760px); color: var(--wt-on-dark);
}
.hero-bg { position: absolute; inset: 0; z-index: 0; background: var(--wt-navy); }

/* Hero carousel — calmed down 14 July at Soana's request ("minimal, not
   overbearing"). This used to be a jagged kupesi-zigzag clip-path wipe
   plus a light streak sweeping the seam; both are gone now. Once each
   slide carried its own real copy (not one shared heading), that fast,
   busy transition was one motion too many on top of the text changing —
   so the whole thing steps down to a single plain opacity crossfade, and
   the only other movement left is a very slow, subtle zoom (ambient, not
   an "event" — nothing to notice happening, just a photo that feels
   alive) that settles right as the slide's turn ends. JS only ever
   toggles .is-active — no clip-path interpolation, no separate sweep
   element, no wipe timer to babysit. Falls back to the first slide only
   if JS never runs; the zoom is dropped entirely under reduced motion. */
.hero-carousel { position: absolute; inset: 0; z-index: 1; overflow: hidden; }
.hero-carousel-slide {
	position: absolute; inset: 0; width: 100%; height: 100%;
	object-fit: cover; z-index: 1;
	opacity: 0;
	transform: scale(1.04);
	transition: opacity 1.4s ease, transform 7s linear;
}
.hero-carousel-slide.is-active { opacity: 1; z-index: 2; transform: scale(1); }
.hero-scrim {
	position: absolute; inset: 0; z-index: 2; pointer-events: none;
	background:
		/* Top blend (14 July): fades the utility-bar/nav's solid navy down
		   into the carousel photo, so the transparent header doesn't cut
		   hard against the image — the hero now reads as emerging from
		   behind the navy bar rather than starting abruptly beneath it. */
		linear-gradient(180deg, rgba(12,19,48,0.92) 0%, rgba(12,19,48,0.55) 8%, rgba(12,19,48,0) 20%),
		linear-gradient(100deg, rgba(6,10,26,0.90) 0%, rgba(6,10,26,0.72) 30%, rgba(6,10,26,0.32) 56%, rgba(6,10,26,0.08) 76%),
		/* Bottom floor (14 July, hardened for the on-photo trust ribbon):
		   deeper than the old 0.55 flat stop so the ribbon's text has a
		   guaranteed dark floor to sit on across all 3 slides, not just
		   whichever one happens to be dim there already. Mirrors the top
		   blend above — the hero now has a deliberate top-and-bottom
		   vignette bookending the photo instead of one-off patchwork. */
		linear-gradient(0deg, rgba(6,10,26,0.85) 0%, rgba(6,10,26,0.5) 16%, transparent 36%);
}
.hero-carousel-dots {
	/* Homepage revamp (embed pass) — Soana: move the dots to bottom-middle
	   at every width (they'd only lived on the left-edge rail ≥980px,
	   reverting to a bottom-center row below that — see the removed
	   980px override that used to do this repositioning, folded into this
	   base rule instead) and bump them up one size step (8px dot/8px
	   padding → 10px dot/9px padding) so they read clearly as controls
	   without turning into a distracting row of large blobs over the
	   photo. .hero-copy-stack/.hero-actions no longer need a left offset
	   to clear a rail that isn't there any more (see below). */
	position: absolute; z-index: 3; left: 50%; bottom: 26px; transform: translateX(-50%);
	display: flex; flex-direction: row; align-items: center; gap: 12px;
}
.hero-carousel-dots::before {
	/* Hairline rail behind the dots — without it the dots read as loose
	   marks floating in the photo; the line ties them into one component
	   (a track you move a position along). Sized to the dot row's own
	   width via inset:0 so it never needs separate tuning when a slide is
	   added. */
	content: ""; position: absolute; inset: 0; margin: auto;
	background: rgba(255,255,255,0.32);
	height: 1px;
}
.hero-carousel-dot {
	/* A real <button> (clickable/focusable so a user can jump straight to
	   a slide, see theme.js goTo()). Padding pads out the hit target well
	   past the small visible dot itself. */
	appearance: none; border: none; background: transparent; margin: 0;
	padding: 9px; display: flex; align-items: center; justify-content: center;
	cursor: pointer; border-radius: 50%;
}
.hero-carousel-dot::before {
	content: ""; display: block; width: 10px; height: 10px; border-radius: 50%;
	background: rgba(255,255,255,0.4); box-shadow: 0 0 0 1px rgba(0,0,0,0.15);
	transition: background 0.3s ease, transform 0.3s ease;
}
.hero-carousel-dot:hover::before { background: rgba(255,255,255,0.7); }
.hero-carousel-dot:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.hero-carousel-dot.is-active::before { background: #fff; transform: scale(1.25); }
@media (prefers-reduced-motion: reduce) {
	.hero-carousel-slide {
		transform: none !important;
		transition: opacity 0.4s linear;
	}
	.hero-carousel-slide.is-active { opacity: 1; }
}

/* Real kupesi (tapa stencil) artwork — a genuine 3-diamond ngatu motif,
   recoloured from its source red to the company's own brand purple and
   trimmed tip-to-tip so it tiles seamlessly (assets/images/kupesi-band.png).
   NOT currently used anywhere in the markup — it sat as a divider strip
   right under the hero, which the user didn't like there, so it's been
   pulled out (see PROJECT-NOTES.md). Left defined here, unused, in case
   it's wanted for a different spot later (Partners ribbon border and a
   faint Why WanTok watermark have both been floated previously). */
.tapa-band {
	height: 46px; width: 100%;
	background-color: var(--wt-bg);
	background-image: url("assets/images/kupesi-band.png");
	background-repeat: repeat-x;
	background-position: left center;
	background-size: auto 100%;
	box-shadow: 0 -1px 0 rgba(12,19,48,0.12);
}

.hero-inner { position: relative; z-index: 3; padding: 120px 22px; width: 100%; }
/* Per-slide copy (14 July): each slide now has its own eyebrow/title
   instead of one static block shared across all 4 — text changes along
   with the photo, in sync with theme.js goTo(). Stacked absolutely (like
   the carousel images themselves) rather than swapping text in place, so
   the outgoing and incoming copy crossfade instead of jump-cutting.
   min-height on the stack reserves room for the tallest slide's copy so
   switching slides doesn't visibly resize/jiggle the block. Re-measured
   15 July after the lead paragraph was dropped — the old 320px value was
   sized for eyebrow+title+paragraph and left a dead gap above the CTA
   buttons once the paragraph was cut (worse on 2-line titles like slide
   1's than on 3-line ones). Tallest slide (a 3-line title) now measures
   ~260px; bumped to 264px for a little rounding room. margin-left clears
   the left-hand dot rail. */
/* --hero-title-fs drives the title's font-size AND (below) the copy
   block's width and reserved height, all from one source of truth. Fixed
   px values for the container width and reserved height fell out of sync
   with the font at viewport widths between the mobile and "wide desktop"
   breakpoints — a smaller scaled-down font sitting inside a box sized
   for the biggest font left a large dead gap above the buttons on
   in-between/laptop-width screens (15 July, reported as "small and
   awkward" with a big gap to the buttons). Tying width/height to the same
   variable that drives the font means every screen size scales together. */
.hero-copy-stack {
	/* 27 Aug — Soana asked for the hero headline to run "a bit smaller";
	   all three clamp() values scaled down ~15% together (min/preferred/
	   max) so it stays proportional at every viewport width rather than
	   just capping the top end. */
	--hero-title-fs: clamp(3.05rem, 7.2vw, 5.4rem);
	/* Homepage revamp (embed pass) — margin-left was 64px to clear the old
	   left-edge dot rail; the dots now live bottom-center (see
	   .hero-carousel-dots above) so there's no rail to clear any more. */
	position: relative; margin-left: 0;
	/* 4 lines at 1.2 line-height, plus eyebrow + the two margins below it.
	   27 Aug — bumped from 3.6 (3 lines) to 4.8 (4 lines). Found while
	   verifying the "make the hero text smaller" request: 3 of the 5
	   slides' titles ("Fast satellite internet, wherever you are.",
	   "Smart business technology. Serious protection.", "Utility
	   management, right from your phone.") actually wrap to 4 lines at
	   this box's 9.15em max-width, not 3 — they were added 8-15 July,
	   after the min-height comment above them was last measured against
	   only the (2-3 line) slides that existed then, and nobody re-checked
	   it against the longer titles added the same day. Left unfixed, the
	   4th line spilled straight into the Contact Us/See our services
	   buttons below (confirmed both before and after the font-size
	   change — same absolute overlap either way, since box width and
	   font-size scale together via the same --hero-title-fs variable, so
	   line-wrapping is independent of the font-size request itself). */
	min-height: calc(var(--hero-title-fs) * 4.8 + 3.2rem);
}
.hero-copy {
	/* Slowed from 0.45s to match the calmer photo crossfade above (14
	   July) — the text and the photo now settle at roughly the same
	   pace instead of the words snapping in noticeably faster. Width is
	   set on .hero-title itself (below) in em, not here in px, so it
	   scales with the title's own font-size instead of drifting out of
	   proportion with it at in-between viewport widths.
	   27 Aug — anchored from the BOTTOM of the reserved stack (was `top:
	   0`) per Soana's request to have the text sit right on top of the
	   Contact Us/See our services buttons instead of top-anchored with a
	   gap underneath. Since the stack's min-height is sized for the
	   longest (4-line) title (see .hero-copy-stack above), a top anchor
	   left shorter 2-line titles with a big empty gap between the text
	   and the buttons below; bottom-anchoring means every slide's text —
	   however many lines — sits flush against the same fixed distance
	   from the buttons. */
	position: absolute; bottom: 0; left: 0; right: 0;
	opacity: 0; pointer-events: none; transition: opacity 1s ease;
}
.hero-copy.is-active { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) {
	.hero-copy { transition: none; }
}
.hero-eyebrow {
	font-family: var(--wt-font-display); font-weight: 600; letter-spacing: 0.04em;
	text-transform: uppercase; font-size: 0.86rem; color: var(--wt-cyan);
	margin-bottom: 0.65rem;
}
/* Bumped three times on 15 July chasing "still too small" feedback before
   finding the actual cause: Elementor's global Kit CSS ships
   `.elementor-kit-1 h1 { font-size: 40px; font-weight: 900; }`, which has
   higher specificity than a plain `.hero-title` class selector, so it was
   silently overriding every size change made here regardless of the
   value. Scoped to "body.wp-theme-wantok-new" (see the h1-h4 rule near
   the top of this file) so it actually wins. 9.15em max-width keeps
   roughly the same wrap point at every size since it scales with this
   same font-size. Margins on the eyebrow/title/buttons stay tightened so
   the block reads as one group. */
body.wp-theme-wantok-new .hero-title { font-size: var(--hero-title-fs); font-weight: 700; max-width: 9.15em; margin-bottom: 0.85rem; color: #fff; }
.hero-actions {
	/* A sibling of .hero-copy-stack, not nested inside each slide's
	   .hero-copy, with its own top margin — no lead paragraph sits
	   between title and buttons any more (dropped 15 July), but the
	   stack's height is still driven by the tallest title, so this stays
	   independent rather than relying on inherited spacing. Shares
	   .hero-copy-stack's 64px left offset (15 July fix) so the buttons
	   line up under the headline instead of under the logo/container
	   edge above — before this they only matched the dot rail's margin
	   by coincidence and drifted out of line with the title. Margin-top
	   tightened from 1.8rem (15 July) to sit closer under the title. */
	display: flex; gap: 0.8rem; flex-wrap: wrap; margin-top: 1rem; margin-left: 0;
}

/* Page media — the illustrated brand imagery used inline on inner pages
   (not the homepage hero, which is the full-bleed carousel above). A
   gentle hover "rollover" (scale) is the only interaction. */
.richtext-media {
	position: relative; border-radius: var(--wt-radius); overflow: hidden;
	box-shadow: var(--wt-shadow-lg); aspect-ratio: 4 / 3; width: 100%;
}
.richtext-media img {
	display: block; width: 100%; height: 100%; object-fit: cover;
	transition: transform 0.5s ease;
}
.richtext-media:hover img { transform: scale(1.045); }
.richtext-media--contain { background: var(--wt-surface); }
.richtext-media--contain img { object-fit: contain; padding: 18px; }

/* Optional 'media_fill' flag — stretches the media column to match the
   full height of its row instead of sitting at a fixed 4:3 box, for a
   block whose copy column runs noticeably taller than a standard aspect-
   ratio image would (first use: Anker Solix's X1 illustration, next to
   two paragraphs + a 6-item list — see wantok_block_richtext() in
   page-content-render.php). min-height keeps it from going too short if a
   future use of this flag pairs it with very little copy. */
.richtext-section--media-fill.has-media { align-items: stretch; }
.richtext-section--media-fill .richtext-media { aspect-ratio: auto; height: 100%; min-height: 320px; }
@media (max-width: 980px) {
	/* Below this width .richtext-section.has-media collapses to a single
	   column (see that rule further down this file), so the media column
	   is no longer "next to" the copy — back to a normal fixed-ratio image. */
	.richtext-section--media-fill .richtext-media { height: auto; aspect-ratio: 4 / 3; }
}

/* ---------- Trust strip ----------
   14 July, round 10: after trying a centred headline+paragraph intro
   (round 7) and then a split-head layout matching Why WanTok (round 9),
   Soana didn't like either once she saw them live and asked to drop any
   heading entirely — just the stat row, straight after the hero, with
   plain white space above and below from this section's own padding
   (no bespoke intro markup left to remove padding rules for). The
   closing "See what's included" text link (round 9) is gone too — this
   section makes its case with the stat row alone now, no CTA of its
   own. Round 8's change stands: icons are one colour (navy), not
   cycled — bare strokes in a single row read as noise with 4 hues,
   unlike Services' cards where each icon sits in its own coloured
   badge. */
.trust-strip { background: var(--wt-navy); padding: 44px 0 48px; }
.trust-inline-list {
	display: grid; grid-template-columns: repeat(4, 1fr); list-style: none;
	margin: 0; padding: 0;
}
.trust-inline-item {
	display: flex; flex-direction: column; align-items: flex-start; justify-content: center;
	gap: 10px; padding: 0 2rem;
}
.trust-inline-item:not(:first-child) { border-left: 1px solid rgba(255,255,255,0.18); }
.trust-inline-icon { flex: none; color: rgba(255,255,255,0.55); }
.trust-inline-text { display: flex; flex-direction: column; gap: 4px; }
.trust-inline-label {
	font-family: var(--wt-font-display); font-weight: 700; font-size: 1.32rem;
	letter-spacing: -0.01em; color: #fff; white-space: nowrap;
}
.trust-inline-detail { font-size: 0.85rem; color: rgba(255,255,255,0.62); white-space: nowrap; }

/* ---------- Section heads ----------
   Used to carry a small purple zigzag "kupesi" mark before every eyebrow
   (a nod to the carved stencils used to stamp triangle/diamond borders
   onto ngatu/tapa cloth). Removed everywhere 14 July — Soana didn't like
   it on Why WanTok specifically ("makes no sense", plus it broke the
   monochrome direction the rest of the page has been moving toward all
   session) and since it was the exact same mark on every section
   (Services/Plans/Partners/News too), it came out site-wide rather than
   leaving Why WanTok as the one section without it. */
.section-head { max-width: 720px; margin: 0 auto 2.8rem; text-align: center; }
.section-head-row { display: flex; align-items: flex-end; justify-content: space-between; max-width: none; text-align: left; gap: 1rem; flex-wrap: wrap; }
.section-eyebrow {
	font-family: var(--wt-font-display); font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
	font-size: 0.82rem; color: var(--wt-blue); margin-bottom: 0.7rem;
	display: inline-flex; align-items: center; gap: 0.55em;
}
.section-head-row .section-eyebrow, .section-head-row .section-title { display: block; }
.section-head-row .section-eyebrow { display: inline-flex; }
.section-title { font-size: clamp(2rem, 3.8vw, 2.9rem); letter-spacing: -0.015em; line-height: 1.08; }
.section-intro { color: var(--wt-ink-2); font-size: 1.08rem; }

/* ---------- Services ----------
   Round 15 (14 July) — Soana shared a clean 3x2 reference (icon, bold
   title, quiet description, small "learn more" link, all bare — no card
   shell, no coloured badges) and asked for it plus two new services
   (Solar Energy, Utility Management System). 6 services now, laid out
   3 columns x 2 rows to match the reference exactly. No background, no
   padding box, no shadow — just icon-over-text sitting directly on the
   page, same monochrome-navy icon treatment as the trust strip. */
.services { padding: 100px 0 108px; background: var(--wt-surface); }
/* Round 20 (14 July) — flat left-to-right row instead of a centred,
   stacked title+subtitle+intro block (that read as "towering" over the
   grid below it). Body copy on the left, a vertical divider, then the
   title+subheading on the right — same navy pair as Why WanTok's
   title/subtitle, just arranged horizontally instead of on top of the
   body copy. */
.services-head {
	display: flex; align-items: center; gap: 2.6rem;
	max-width: none; margin: 0 0 2.8rem; text-align: left;
}
.services-body { flex: 1 1 380px; margin: 0; }
.services-divider { width: 1px; align-self: stretch; background: var(--wt-border); flex: none; }
.services-heading { flex: 1 1 380px; }
/* Boosted specificity — the general body.wp-theme-wantok-new h1-h4 rule
   above (added 15 July to beat Elementor's global Kit CSS) has higher
   specificity than a plain single class, so it was overriding this back
   to the default dark ink color. Same fix applied to every other title/
   heading override below that hit the same regression. */
body.wp-theme-wantok-new .services-title { color: #1c2c68; margin-bottom: 0.4rem; }
.services-subtitle {
	font-family: var(--wt-font-display); font-weight: 600; font-size: 1.3rem;
	color: #3d4f96; letter-spacing: -0.005em; margin: 0;
}
/* Homepage revamp (embed pass) — row-gap widened from 3rem so a lifted
   mascot (.service-char pops ~26px above its own card, see below) never
   reaches into the card directly above it in the 2-row grid. */
.services-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4.2rem 2.6rem; list-style: none; }
.service-card { display: flex; flex-direction: column; align-items: flex-start; }
.service-icon {
	display: flex; align-items: center; justify-content: center;
	width: 56px; height: 56px; border-radius: 50%;
	background: var(--wt-bg); color: var(--wt-navy); margin-bottom: 16px;
	box-shadow: 0 1px 2px rgba(28,44,104,0.08);
}
.service-title { font-size: 1.12rem; margin-bottom: 0.45rem; letter-spacing: -0.01em; }
.service-desc { color: var(--wt-ink-2); font-size: 0.94rem; margin-bottom: 0.9rem; line-height: 1.5; }
.service-link {
	font-weight: 600; font-family: var(--wt-font-display); font-size: 0.9rem;
	color: var(--wt-blue); display: inline-flex; align-items: center; gap: 0.4em;
	padding-bottom: 2px; border-bottom: 1px solid transparent; transition: color 0.2s ease, border-color 0.2s ease;
}
.service-link span { transition: transform 0.18s ease; }
.service-link:hover { color: #1c2c68; border-color: currentColor; }
.service-link:hover span { transform: translateX(3px); }

/* Homepage revamp (embed pass) — each Services card now carries its own
   mascot, reusing the real cutout artwork from the dedicated Services and
   Plans page (see wantok_get_services_showcase()/$wt_service_chars in
   front-page.php) — the full original artwork, lifted ~26px above the
   card's own top edge on a soft drop shadow ("Option D", the version
   Soana approved), landing between a fully-contained treatment and that
   dedicated page's full 46px pop-out. `.services .service-card` needs
   overflow:visible for the lift to show (the base rule above has no
   overflow declared, so nothing to override), and a card-accent glow
   doubles as the "landing pad" the character's drop shadow reads
   against. `.service-title` gets its own margin-top since the
   absolutely-positioned character doesn't take up flow space, so nothing
   else pushes the title down to clear it. */
.services .service-card { overflow: visible; border: 1px solid transparent; border-radius: 14px; padding: 32px 22px 26px; box-shadow: var(--wt-shadow); transition: transform 0.2s ease, box-shadow 0.2s ease; }
.services .service-card:hover { transform: translateY(-4px); box-shadow: var(--wt-shadow-lg); }
.services .service-card.glow-cyan { border-color: color-mix(in srgb, var(--wt-cyan) 42%, transparent); }
.services .service-card.glow-purple { border-color: color-mix(in srgb, var(--wt-purple) 42%, transparent); }
.services .service-char {
	position: absolute; top: -26px; left: 50%; transform: translateX(-50%);
	display: flex; align-items: flex-end; justify-content: center; height: 130px; width: 100%;
}
.services .service-char::before {
	content: ""; position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
	width: 210px; height: 80px; border-radius: 50%; z-index: 0;
	background: radial-gradient(ellipse at center, color-mix(in srgb, var(--glow-c) 24%, transparent) 0%, transparent 75%);
}
.services .service-card.glow-cyan .service-char { --glow-c: var(--wt-cyan); }
.services .service-card.glow-purple .service-char { --glow-c: var(--wt-purple); }
.services .service-char-img {
	position: relative; z-index: 1; max-height: 100%; max-width: 230px; width: auto; height: auto;
	object-fit: contain; object-position: bottom;
	filter: drop-shadow(0 10px 14px rgba(13,19,46,0.18));
}
.services .service-card .service-title { margin-top: 78px; }

/* 4 August — Soana asked for the homepage Services grid to go back to this
   original icon-card design (above). The "glass" photo-card redesign built
   3-4 August (originally ported onto the homepage, then reverted) now lives
   on its own dedicated "Services and Plans" page instead — reached from the
   nav tab of the same name — under these renamed classes so it no longer
   shares selectors with the homepage cards above. See
   wantok_get_services_showcase() in functions.php and the 'services-showcase'
   block in page-content-render.php. */
.services-showcase-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 84px 32px; list-style: none; }
.services-showcase-card {
	position: relative; height: 480px; border-radius: 24px; overflow: visible;
	display: flex; flex-direction: column; justify-content: flex-end; align-items: center;
	text-align: center; background: rgba(28,44,104,0.05);
	-webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
	border: 1px solid rgba(28,44,104,0.14);
	box-shadow: 0 18px 40px rgba(12,19,48,0.1);
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.services-showcase-card:hover { transform: translateY(-4px); box-shadow: var(--wt-shadow-lg); }
/* Fixed 272px-tall box shared by every card; width comes from each photo's
   own natural aspect ratio via an inline `aspect-ratio` style set per card
   in page-content-render.php (wantok_get_services_showcase()' 'aspect'
   value) — combined with the fixed height, object-fit: contain then fills
   the box's full height with no letterboxing, so every photo overlaps the
   card by the same ~46px regardless of whether it's a tall portrait or a
   wide scene. Swapping a photo later just means updating that one
   aspect-ratio value. */
.services-showcase-photo {
	position: absolute; top: -46px; left: 50%; transform: translateX(-50%);
	height: 272px;
	/* 27 August — was a flat `max-width: 460px`. That was sized against the
	   old 3-column grid's own card width; in the new 4-column grid (see
	   .services-showcase-grid above) cards run narrower, and a couple of
	   photos are wide-aspect (Cybersecurity's cutout, and Innovation Hub's
	   floor-plan illustration) — at a flat 460px cap those overflowed past
	   their own card's edges. min() keeps 460px as the cap on wider cards
	   (tablet's 2-column grid, see the 980px breakpoint below) while
	   shrinking to fit the card itself once it's narrower than that, since
	   % here resolves against .services-showcase-card (the nearest
	   positioned ancestor). */
	max-width: min(460px, 94%);
}
.services-showcase-photo img {
	width: 100%; height: 100%; display: block;
	object-fit: contain; object-position: center bottom;
	/* 5 August — was drop-shadow(0 16px 18px rgba(12,19,48,0.28)). Each photo's
	   character-plus-props composition already spans nearly the full width and
	   height of its box, so that strong a blur smoothed the whole silhouette
	   into a soft rectangular "halo" behind the photo instead of a natural
	   contact shadow (Soana flagged this as a visible fade/box around the
	   images). A lighter, tighter shadow still grounds the photo without
	   reading as a box. */
	filter: drop-shadow(0 6px 8px rgba(12,19,48,0.14));
}
/* 27 August — for a full-bleed banner photo with no transparent padding
   around it. Not currently used by any card (Innovation Hub — Tonga
   moved to a regular illustrated cutout, same as the other seven — see
   wantok_get_services_showcase() in functions.php), kept dormant in case
   a future partner card needs a baked-in-headline banner treatment
   again. */
.services-showcase-photo--banner img { object-position: center calc(100% - 60px); }
.services-showcase-body { position: relative; width: 100%; padding: 0 26px 32px; margin-top: 260px; }
body.wp-theme-wantok-new .services-showcase-title { color: #1c2c68; font-size: 1.2rem; margin: 0 0 10px; letter-spacing: -0.01em; }
.services-showcase-desc { color: var(--wt-ink-2); font-size: 0.92rem; line-height: 1.55; margin: 0 0 22px; }
.services-showcase-arrow {
	width: 44px; height: 44px; border-radius: 50%; border: 1.5px solid #d7dcec;
	display: flex; align-items: center; justify-content: center; margin: 0 auto;
	color: var(--wt-navy); text-decoration: none; transition: background 0.2s ease, color 0.2s ease;
}
.services-showcase-arrow:hover { background: var(--wt-navy); border-color: var(--wt-navy); color: #fff; }
.services-showcase-arrow svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* 5 August — replaces the padding this section used to get for free from
   `.richtext-section` (dropped — see the comment above this block's markup
   in page-content-render.php for why). Bottom padding gives the grid room
   to breathe before the footer.
   11 August — top padding bumped from 24px to 90px specifically for the
   Services and Plans page (the only page that uses this section — see
   page-content-render.php): each card's photo floats up to 46px above
   its own card via .services-showcase-photo's top:-46px, so with only
   24px of top padding the photo's highest point actually sat 22px above
   this section's own top edge — overlapping the reel-hero/wave directly
   above it (see .services-reel-hero above) rather than clearing it. 90px
   leaves a clean ~44px gap between the wave and the nearest photo. */
.services-showcase-section { padding: 90px 0 72px; }
/* 5 August — the full-width Samsung/Innovation Hub card (Innovation Hub —
   Tonga, added via the 'wide' flag in wantok_get_services_showcase()).
   Ported from services-plans-glasscard-mockup.html's .mock-card--feature,
   which itself went through three layout options (side-by-side crop, full-
   bleed scrim+overlay text, and this one) before Soana picked "use the
   supplied banner as-is, plain card frame" for consistency with the white
   cards elsewhere, then asked for a title/description/arrow footer to be
   added back in for parity with the other six cards. Spans the full grid
   row; the banner photo runs edge-to-edge at the card's own width (not the
   fixed floating-photo box the other six use — that image already has its
   own baked-in headline/logo, no cutout/aspect-ratio needed) with a plain
   footer strip below it. */
.services-showcase-card--wide {
	grid-column: 1 / -1; height: auto; min-height: 0;
	display: flex; flex-direction: column; align-items: stretch;
	padding: 0; overflow: hidden;
}
.services-showcase-wide-photo { display: block; width: 100%; height: auto; }
.services-showcase-wide-body {
	display: flex; align-items: center; justify-content: space-between;
	gap: 28px; padding: 32px 48px;
}
.services-showcase-wide-text { flex: 1 1 auto; }
.services-showcase-wide-body .services-showcase-title { margin: 0 0 6px; }
.services-showcase-wide-body .services-showcase-desc { margin: 0; max-width: 560px; }
.services-showcase-wide-body .services-showcase-arrow { margin: 0; flex: 0 0 auto; }
@media (max-width: 640px) {
	.services-showcase-wide-body { flex-direction: column; align-items: flex-start; }
}

/* ---------- Plans ----------
   3 cards on the plain white page canvas — the two side plans are told
   apart only by a quiet --wt-surface tint (no border, no default shadow),
   and the featured "Business Connect" card is filled with the brand
   gradient itself, so the one plan we want to sell hardest is also the
   one place this row breaks into full colour.
   NOTE (28 July): .plans / .plans-head / .plans-heading / .plans-divider /
   .plans-body / .plans-title / .plans-subtitle below are now unused —
   the standalone Plans section was merged into Services (see above) and
   its header replaced by a .partners-caption divider. .plans-grid/.plan-card
   and their responsive overrides are still live, reused inside the merged
   section. Left the unused rules in place rather than risk breaking a
   cross-reference under time pressure; safe to prune in a follow-up pass. */
.plans { padding: 80px 0; background: var(--wt-bg); }
/* Round 21 (14 July) — same flat row header as Services (Option A):
   heading left, divider, intro right, same navy title/subtitle pair. */
.plans-head {
	display: flex; align-items: center; gap: 2.6rem;
	max-width: none; margin: 0 0 2.8rem; text-align: left;
}
.plans-heading { flex: 1 1 200px; }
.plans-divider { width: 1px; align-self: stretch; background: var(--wt-border); flex: none; }
.plans-body { flex: 1 1 380px; margin: 0; }
body.wp-theme-wantok-new .plans-title { color: #1c2c68; margin-bottom: 0.4rem; }
.plans-subtitle {
	font-family: var(--wt-font-display); font-weight: 600; font-size: 1.3rem;
	color: #3d4f96; letter-spacing: -0.005em; margin: 0;
}
.plans-grid { display: grid; grid-template-columns: repeat(3, 1fr); align-items: stretch; gap: 1.3rem; }
/* Redesigned 10 August (Soana: Signal Bars card shape + Tide Ledger's
   inline price line + Compare Rail's corner ribbon — see PROJECT-NOTES.md
   / wantok_block_cards() in page-content-render.php for the full history).
   White card + hairline border + soft shadow replaces the old flat
   var(--wt-surface) box so pricing cards read as a distinct, slightly
   elevated object rather than a plain tinted rectangle. overflow:hidden
   is required for the corner-ribbon .plan-flag below. */
.plan-card {
	background: #fff; border: 1px solid var(--wt-border); border-radius: 14px;
	box-shadow: var(--wt-shadow); overflow: hidden;
	padding: 26px 24px; display: flex; flex-direction: column; position: relative;
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.plan-card:hover { transform: translateY(-4px); box-shadow: var(--wt-shadow-lg); }
.plan-featured {
	background: var(--wt-navy); border-color: transparent; color: var(--wt-on-dark);
	box-shadow: 0 24px 48px -16px rgba(12,19,48,0.35);
}
/* Corner ribbon (was a top-left pill before 10 August) — diagonal strip
   tucked into the card's top-right corner; .plan-card's overflow:hidden
   clips the ribbon's overhang to the card's own rounded corner. */
.plan-flag {
	position: absolute; top: 13px; right: -27px; background: var(--wt-cyan); color: var(--wt-navy);
	font-family: var(--wt-font-display); font-weight: 700; font-size: 0.6rem; letter-spacing: 0.06em;
	text-transform: uppercase; padding: 3px 32px; transform: rotate(40deg);
}
/* Name + signal-bars glyph sit on one row so the bars read as part of the
   plan's identity, not a separate stat — see wantok_block_cards() for how
   bar_level is computed per card. */
.plan-top { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }
.plan-name { font-size: 1.15rem; margin: 0; letter-spacing: -0.01em; }
.plan-featured .plan-name { color: #fff; }
.plan-bars { display: inline-flex; align-items: flex-end; gap: 3px; height: 13px; }
.plan-bars span { display: block; width: 5px; border-radius: 2px; background: var(--wt-border); }
.plan-bars span:nth-child(1) { height: 30%; }
.plan-bars span:nth-child(2) { height: 55%; }
.plan-bars span:nth-child(3) { height: 78%; }
.plan-bars span:nth-child(4) { height: 100%; }
.plan-bars span.is-on { background: var(--wt-blue); }
.plan-featured .plan-bars span { background: rgba(255,255,255,0.22); }
.plan-featured .plan-bars span.is-on { background: var(--wt-cyan); }
/* Price is now the visual anchor of the card (was a small ink-3 line) —
   the 'price' string already includes its own unit (e.g. "120 TOP /
   month"), so no markup split was needed to make it read as one line. */
.plan-for {
	font-family: var(--wt-font-display); font-weight: 700; font-size: 1.3rem;
	color: var(--wt-navy); margin: 6px 0 1.1rem; letter-spacing: -0.01em;
}
.plan-featured .plan-for { color: #fff; }
.plan-points { list-style: none; margin-bottom: 1.6rem; display: grid; gap: 0.6rem; }
.plan-points li { position: relative; padding-left: 1.7rem; color: var(--wt-ink-2); font-size: 0.98rem; }
.plan-featured .plan-points li { color: rgba(255,255,255,0.88); }
.plan-points li::before {
	content: ""; position: absolute; left: 0; top: 0.45em; width: 12px; height: 7px;
	border-left: 2px solid var(--wt-blue); border-bottom: 2px solid var(--wt-blue);
	transform: rotate(-45deg);
}
.plan-featured .plan-points li::before { border-color: #fff; }
.plan-card .btn { margin-top: auto; }
.plan-featured .btn-primary { background: #fff; color: var(--wt-navy); box-shadow: none; }

/* Homepage revamp (embed pass) — Plans card redesign, approved
   Navy-Dominant mockup: white-card + coloured-ring-glow language replaces
   the old solid-navy-fill "featured" card, with each card's cyan/purple
   glow now carrying real meaning (cyan = Business Connect & Satellite &
   Remote, purple = Home Broadband, the one plan we feature). Scoped with
   an extra `.plans` ancestor rather than editing `.plan-card`/
   `.plan-featured` directly — those two classes are shared site-wide with
   other pages' own plan cards (e.g. the Broadband Plans page), so this
   only touches the homepage's own Plans section. */
.plans .plan-card { background: #fff; color: var(--wt-ink); border: 1px solid transparent; }
.plans .plan-card.glow-cyan {
	border-color: color-mix(in srgb, var(--wt-cyan) 42%, transparent);
	box-shadow: var(--wt-shadow), 0 0 24px color-mix(in srgb, var(--wt-cyan) 18%, transparent);
}
.plans .plan-card.glow-purple {
	border-color: color-mix(in srgb, var(--wt-purple) 42%, transparent);
	box-shadow: var(--wt-shadow), 0 0 24px color-mix(in srgb, var(--wt-purple) 18%, transparent);
}
.plans .plan-featured {
	background: #fff;
	box-shadow: var(--wt-shadow), 0 0 40px color-mix(in srgb, var(--wt-purple) 22%, transparent);
}
.plans .plan-featured .plan-name,
.plans .plan-featured .plan-for { color: var(--wt-ink); }
.plans .plan-name { color: var(--wt-ink); }
.plans .plan-for { color: var(--wt-navy); }
.plans .plan-points li,
.plans .plan-featured .plan-points li { color: var(--wt-ink-2); }
.plans .plan-points li::before,
.plans .plan-featured .plan-points li::before { border-color: var(--wt-blue); }
/* 27 Aug — Soana asked for the Home Broadband card's ribbon + button to be
   navy instead of the accent-blue/cyan they'd had since the Navy-Dominant
   embed pass.
   Button: NOT simply dropping the old `.plans .plan-featured .btn-primary`
   override — there's a second, unscoped `.plan-featured .btn-primary`
   rule (white bg / navy text, further up this file) written for the
   solid-navy-fill `.plan-featured` card used elsewhere on the site (e.g.
   Broadband Plans), which has higher specificity than the plain
   `.btn-primary` base and would've silently won once the accent-blue
   override was removed — tried that first, caught it because the button
   rendered white instead of navy. Explicit navy override below instead.
   Ribbon: see the new `.plans .plan-flag` rule below `.plan-flag`'s base
   definition. */
.plans .plan-featured .btn-primary { background: var(--wt-navy); color: #fff; box-shadow: none; }
.plans .plan-featured .btn-primary:hover { background: var(--wt-navy-2); }

/* Ribbon — base `.plan-flag` (see its own rule, shared site-wide with
   other pages' plan cards) is accent-cyan; scoped here the same way as
   the rest of this `.plans` block so only the homepage's own ribbon
   changes. White text for contrast against navy, same pairing used
   everywhere else navy is a solid fill (utility bar, sticky header, CTA
   band). */
.plans .plan-flag { background: var(--wt-navy); color: #fff; }
/* Widens the pricing-card grid past .richtext-section's normal 900px
   text-column cap — Starlink's 6-card "Local Priority Plans" row was
   cramped at 900px. Same override technique as .richtext-section.has-media
   just below. */
.richtext-section.pricing-cards-section { max-width: var(--wt-maxw); }

/* ---------- Video block ----------
   Added 10 August for the Services and Plans / Broadband Plans / Starlink
   Plans pages. No real footage exists yet, so .video-frame--placeholder
   is the honest default (dashed frame, no invented content) until a real
   'file' is set on the block — see wantok_block_video(). */
.video-section { text-align: center; }
.video-frame {
	position: relative; aspect-ratio: 16 / 9; border-radius: 14px; overflow: hidden;
	background: var(--wt-navy); box-shadow: var(--wt-shadow-lg); margin: 0 auto;
}
.video-frame video { width: 100%; height: 100%; object-fit: cover; display: block; }
.video-frame--placeholder {
	display: flex; align-items: center; justify-content: center;
	background: var(--wt-surface-2); border: 2px dashed var(--wt-border); box-shadow: none;
}
.video-placeholder-inner { text-align: center; color: var(--wt-ink-3); }
.video-placeholder-icon {
	display: flex; align-items: center; justify-content: center; margin: 0 auto 0.6rem;
	width: 52px; height: 52px; border-radius: 50%; background: #fff; color: var(--wt-blue);
	font-size: 1.1rem; box-shadow: var(--wt-shadow);
}
.video-placeholder-inner p { margin: 0; font-size: 0.92rem; }
.video-caption { color: var(--wt-ink-3); font-size: 0.88rem; margin: 0.9rem 0 0; }

/* ---------- Plan tabs ----------
   Added 10 August to cut Starlink Plans' page length — see
   wantok_block_plan_tabs() in page-content-render.php for why this is a
   real content fit (separate categories, not a sequence) and not just
   decoration, and for why the CSS below matches active tab/panel by an
   "ends in -N" id suffix rather than per-instance IDs (only safe with one
   plan-tabs block per page for now).
   Radios are visually hidden but stay in the tab order (no display:none)
   so keyboard users can still reach and operate them; the visible focus
   ring is painted onto the sibling label via :focus-visible below. */
.plan-tab-input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.plan-tabs-nav {
	display: flex; flex-wrap: wrap; gap: 4px; margin: 0 0 1.6rem; padding: 5px;
	border: 0; background: var(--wt-surface); border-radius: 999px;
	/* max-content sizes the pill to its own content on desktop (rather than
	   stretching this <fieldset>'s default block width to the full 1180px
	   container); max-width:100% then caps that at the mobile container's
	   width so flex-wrap can actually kick in instead of silently
	   overflowing past the edge — width:max-content alone defeats wrapping
	   because the flex container just grows past its parent instead. */
	width: max-content; max-width: 100%;
}
.plan-tab-label {
	padding: 9px 20px; border-radius: 999px; font-family: var(--wt-font-display);
	font-weight: 600; font-size: 0.88rem; color: var(--wt-ink-2); cursor: pointer;
	transition: background 0.15s ease, color 0.15s ease; user-select: none;
}
.plan-tab-label:hover { color: var(--wt-navy); }
.plan-tab-panel { display: none; }
/* One rule pair per tab index (0-5 covers this page's 4 tabs with room to
   grow) — see the function doc comment for why suffix matching was used
   instead of generating unique per-instance selectors. */
input[id$="-0"]:checked ~ .plan-tabs-nav label[for$="-0"],
input[id$="-1"]:checked ~ .plan-tabs-nav label[for$="-1"],
input[id$="-2"]:checked ~ .plan-tabs-nav label[for$="-2"],
input[id$="-3"]:checked ~ .plan-tabs-nav label[for$="-3"],
input[id$="-4"]:checked ~ .plan-tabs-nav label[for$="-4"],
input[id$="-5"]:checked ~ .plan-tabs-nav label[for$="-5"] {
	background: var(--wt-navy); color: #fff;
}
input[id$="-0"]:checked ~ .plan-tabs-panels #panel-0,
input[id$="-1"]:checked ~ .plan-tabs-panels #panel-1,
input[id$="-2"]:checked ~ .plan-tabs-panels #panel-2,
input[id$="-3"]:checked ~ .plan-tabs-panels #panel-3,
input[id$="-4"]:checked ~ .plan-tabs-panels #panel-4,
input[id$="-5"]:checked ~ .plan-tabs-panels #panel-5 {
	display: block;
}
input[id$="-0"]:focus-visible ~ .plan-tabs-nav label[for$="-0"],
input[id$="-1"]:focus-visible ~ .plan-tabs-nav label[for$="-1"],
input[id$="-2"]:focus-visible ~ .plan-tabs-nav label[for$="-2"],
input[id$="-3"]:focus-visible ~ .plan-tabs-nav label[for$="-3"],
input[id$="-4"]:focus-visible ~ .plan-tabs-nav label[for$="-4"],
input[id$="-5"]:focus-visible ~ .plan-tabs-nav label[for$="-5"] {
	outline: 3px solid var(--wt-blue); outline-offset: 2px; border-radius: 999px;
}

/* ---------- Collapsible table group ----------
   Added 10 August alongside plan-tabs, same "reduce Starlink's vertical
   scroll" pass — reuses the .faq-item <details> idiom already on the site
   rather than inventing a new disclosure pattern. */
.table-details {
	border: 1px solid var(--wt-border); border-radius: 12px; background: var(--wt-bg);
	padding: 0 20px;
}
.table-details summary {
	cursor: pointer; padding: 18px 4px; font-weight: 700; font-family: var(--wt-font-display);
	color: var(--wt-navy); list-style: none; position: relative;
}
.table-details summary::-webkit-details-marker { display: none; }
.table-details summary::after {
	content: "+"; position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
	color: var(--wt-blue); font-size: 1.2rem; font-weight: 700;
}
.table-details[open] summary::after { content: "\2013"; }
.table-details-body { padding-bottom: 20px; }
.table-details-body h3 { font-size: 1rem; color: var(--wt-navy); margin: 1.4rem 0 0.6rem; }
.table-details-body h3:first-child { margin-top: 0.2rem; }

/* ---------- Why WanTok ----------
   Round 26 (14 July) — restructured to match Services/Plans/News' flat
   header row (heading left, divider, intro right) followed by full-width
   content below, instead of the old side-by-side text-vs-cards two-column
   layout that spent 5+ rounds fighting a height-mismatch problem (two
   independently-sized blocks next to each other only look right when
   their heights happen to match). Stacking the cards below the header
   instead of beside it removes that problem entirely and brings this
   section in line with the rest of the page. Cards are a horizontal row
   now, same navy fill as before (the one deliberate colour break from
   Services' monochrome-icon cards — a plain solid card, not a return to
   the cyan/blue/purple cycle), sized generously again since they're no
   longer competing with the text column for vertical space. */
/* 27 Aug — Soana asked for a light blue background here, same as the
   surrounding sections. Using `var(--wt-surface)`, the same light-blue
   tint `.services`/`.latest-news` already use, rather than inventing a
   new colour — keeps the sitewide palette to the values already defined
   near the top of this file. */
.why { padding: 88px 0 96px; background: var(--wt-surface); }
.why-head {
	display: flex; align-items: center; gap: 2.6rem;
	max-width: none; margin: 0 0 2.8rem; text-align: left;
}
.why-heading { flex: 1 1 200px; }
.why-divider { width: 1px; align-self: stretch; background: var(--wt-border); flex: none; }
.why-body { flex: 1 1 380px; }
/* "Why WanTok?" is the big title itself, with "A technology partner that
   knows Tonga" as a subheading underneath, instead of a small uppercase
   eyebrow above a big title. Recoloured out of the default dark-ink
   heading colour — var(--wt-navy)/--wt-navy-2 (the card fill colours)
   are so dark that as small-area text on white they just read as black,
   not navy, so a lighter, more saturated navy is used here instead while
   staying in the same family as the cards. */
body.wp-theme-wantok-new .why-title {
	font-size: clamp(2.2rem, 4vw, 3.1rem); letter-spacing: -0.015em; line-height: 1.08;
	margin-bottom: 0.4rem; color: #1c2c68;
}
.why-subtitle {
	font-family: var(--wt-font-display); font-weight: 600; font-size: 1.3rem;
	color: #3d4f96; letter-spacing: -0.005em; margin: 0;
}
.why-intro { max-width: none; font-size: 1.1rem; margin-bottom: 0; }
.why-intro-2 { margin-top: 1rem; }
.why-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.6rem; list-style: none; margin: 0; }
/* Homepage revamp (embed pass) — "Ambient Field" (Option J2, the direction
   Soana approved): no card shell at all — each reason is a duotone icon
   inside a soft glow halo, with a wider, softer wash of the same colour
   behind the whole item, fading into the page rather than a border/
   divider marking it off. Entrance animation reuses the theme's own
   generic .reveal-init/.is-visible scroll-reveal (theme.js already
   targets `.why-list li` — see the "Reveal-on-scroll" comment near the
   top of this file) rather than a bespoke observer; only the idle
   halo-pulse and hover response below are new. */
.why-list li {
	background: none; box-shadow: none; border-radius: 0;
	padding: 16px 18px 14px; position: relative; text-align: center;
	display: block;
}
.why-ambient-field {
	position: absolute; inset: -3px 6px; border-radius: 20px; z-index: 0;
	background: radial-gradient(ellipse 70% 65% at 50% 38%, color-mix(in srgb, var(--glow-c) 13%, transparent) 0%, transparent 72%);
	transition: opacity 0.3s ease;
}
.why-list li:hover .why-ambient-field { opacity: 1.6; }
.why-list li > * { position: relative; z-index: 1; }
.why-spotlight {
	position: relative; display: flex; align-items: center; justify-content: center;
	width: 60px; height: 60px; margin: 0 auto 8px; transition: transform 0.3s ease;
}
.why-spotlight::before {
	content: ""; position: absolute; inset: 0; border-radius: 50%;
	background: radial-gradient(circle, color-mix(in srgb, var(--glow-c) 38%, transparent) 0%, transparent 72%);
	transition: opacity 0.3s ease; animation: why-halo-pulse 4.2s ease-in-out infinite;
}
@keyframes why-halo-pulse { 0%, 100% { transform: scale(1); opacity: 0.85; } 50% { transform: scale(1.07); opacity: 1; } }
.why-icon { position: relative; z-index: 1; display: flex; margin: 0 auto; }
.why-list li:hover .why-spotlight { transform: translateY(-3px); }
.why-list li:hover .why-spotlight::before { animation-play-state: paused; opacity: 1; transform: scale(1.12); }
.why-list li p { color: var(--wt-ink-2); }
.why-list h3 { color: var(--wt-ink); font-size: 1.05rem; margin-bottom: 0.4rem; }
.why-list p { margin: 0; color: var(--wt-ink-2); font-size: 0.92rem; line-height: 1.55; }
.why-list li.glow-cyan { --glow-c: var(--wt-cyan); }
.why-list li.glow-purple { --glow-c: var(--wt-purple); }

/* ---------- Partners marquee ----------
   Round 16 (14 July) — no longer its own section between Plans and Latest
   News. Moved up into Services, right under the services grid, as a
   trailing strip of that section instead of a standalone one — so the
   heading is a small caption, not a full eyebrow+h2 section-head, and it
   picks up Services' own padding/background rather than having its own.
   Overflow is hidden here (rather than on the whole section) since the
   marquee-track is intentionally wider than its container and shouldn't
   push the page width out. Continuously auto-scrolling strip of partner
   names; the track's item list is rendered twice by PHP (front-page.php)
   and this animates it exactly one list-width to the left, so the loop
   point is invisible — no JS needed. Pauses on hover/focus and respects
   reduced motion. */
.partners-inline { margin-top: 3rem; overflow: hidden; }
/* Round 17 (14 July) — centred instead of left-aligned, with a divider
   line either side to section it off from the services grid above.
   Round 19 — recoloured navy (to match the cards above) instead of blue. */
.partners-caption {
	display: flex; align-items: center; justify-content: center; gap: 1.2rem;
	margin: 0 0 1.6rem; text-align: center;
}
.partners-caption::before, .partners-caption::after {
	content: ""; flex: 1 1 0; max-width: 140px; height: 1px; background: var(--wt-border);
}
.partners-caption span {
	font-family: var(--wt-font-display); font-weight: 600; font-size: 0.82rem;
	letter-spacing: 0.05em; text-transform: uppercase; color: #1c2c68; white-space: nowrap;
}
.marquee {
	position: relative; padding: 10px 0;
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee-track {
	display: flex; align-items: center; width: max-content; gap: 2.2rem;
	animation: wt-marquee 32s linear infinite;
}
.marquee:hover .marquee-track, .marquee:focus-within .marquee-track { animation-play-state: paused; }
.marquee-item {
	font-family: var(--wt-font-display); font-weight: 600; font-size: 1.2rem; color: var(--wt-ink-2);
	white-space: nowrap; letter-spacing: -0.01em; display: flex; align-items: center; gap: 2.2rem;
}
.marquee-item::after { content: ""; width: 5px; height: 5px; border-radius: 999px; background: var(--wt-border); flex: none; }
.marquee-item:last-child::after { display: none; }
@keyframes wt-marquee {
	from { transform: translateX(0); }
	to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
	.marquee-track { animation: none; overflow-x: auto; }
}

/* ---------- Latest news ----------
   Closes out the plain white run. Cards mirror a minimal blog-card
   layout: the image sits edge-to-edge at the top of the card (no inset
   padding), a quiet date/meta row and bold title sit in a padded block
   below — no excerpt, no border, no default shadow, just a --wt-surface
   tint to tell the card from the page. A single centered button below
   the grid replaces the old corner "All news" link. */
.latest-news { padding: 80px 0; background: var(--wt-surface); }
/* Round 22 (14 July) — same flat row header as Services/Plans (Option A):
   heading left, divider, intro right, same navy title/subtitle pair. */
.news-head {
	display: flex; align-items: center; gap: 2.6rem;
	max-width: none; margin: 0 0 2.8rem; text-align: left;
}
.news-heading { flex: 1 1 200px; }
.news-divider { width: 1px; align-self: stretch; background: var(--wt-border); flex: none; }
.news-head-intro { flex: 1 1 380px; margin: 0; }
body.wp-theme-wantok-new .news-head-title { color: #1c2c68; margin-bottom: 0.4rem; }
.news-subtitle {
	font-family: var(--wt-font-display); font-weight: 600; font-size: 1.3rem;
	color: #3d4f96; letter-spacing: -0.005em; margin: 0;
}
.news-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; list-style: none; margin-top: 0; }
.news-card {
	position: relative; background: var(--wt-bg); box-shadow: 0 1px 3px rgba(28,44,104,0.08);
	border-radius: 6px; overflow: hidden; transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.news-card:hover { transform: translateY(-4px); box-shadow: var(--wt-shadow-lg); }
.news-card-link { display: block; color: inherit; }
.news-thumb { margin: 0; }
.news-thumb img { display: block; width: 100%; height: 180px; object-fit: cover; transition: transform 0.4s ease; }
.news-card-link:hover .news-thumb img { transform: scale(1.05); }
.news-body { padding: 20px 22px 24px; }
.news-card .news-date { display: block; font-size: 0.82rem; color: var(--wt-ink-3); }
.news-title { font-size: 1.2rem; margin: 0.5rem 0 0; letter-spacing: -0.01em; }
.news-card-link:hover .news-title { color: var(--wt-blue); }
.news-empty { color: var(--wt-ink-2); margin-top: 1.5rem; }
.news-cta { text-align: center; margin-top: 2.6rem; }

/* ---------- News page / archive card grid (16 July) ----------
   Reuses the homepage's .news-grid/.news-card component (same card
   language site-wide) rather than the old two-column .post-card list —
   see index.php and archive.php. The archive context adds an excerpt and
   a "Read more" link that the homepage teaser cards don't have room for;
   these are additive rules only, the plain homepage .news-card is
   untouched. Section sits on the same --wt-surface tint as the homepage's
   Latest News band, right below the navy .page-title-banner, so the page
   still reads as "navy banner, then a tinted (not plain-white) body" like
   Support and the homepage. */
.news-archive { padding-top: 64px; }
.news-grid-archive .news-thumb img { height: 200px; }
.news-card .news-excerpt { color: var(--wt-ink-2); font-size: 0.92rem; line-height: 1.55; margin: 0.6rem 0 0.9rem; }
.news-card .news-excerpt p { margin: 0; }
.news-card .read-more {
	display: inline-flex; align-items: center; gap: 0.35em;
	font-size: 0.9rem; color: var(--wt-blue);
}
.news-card-link:hover .read-more { color: var(--wt-purple); }

/* News/archive glow accent (27 Aug 2026, Navy-Dominant pass) — same
   coloured-ring-glow border + soft halo every other card grid site-wide
   carries (.plan-card.glow-cyan/-purple, .feature-mosaic-tile.glow-cyan/
   -purple, the Anker Solix .richtext-section--compact.glow-cyan/-purple
   cards). Alternated automatically via :nth-child rather than a per-card
   PHP flag, since News cards come from three different sources (the real
   WP posts loop, archive.php's loop, and the wantok_press_mentions()
   fallback array) — CSS alternation covers all three with one rule.
   Scoped to .news-grid-archive (News/archive pages only) so the
   homepage's plain 3-card "Latest from WanTok" teaser, which reuses the
   same .news-card component, is untouched. */
.news-grid-archive .news-card { border: 1px solid transparent; }
.news-grid-archive .news-card:nth-child(odd) {
	border-color: color-mix(in srgb, var(--wt-cyan) 40%, transparent);
	box-shadow: 0 1px 3px rgba(28,44,104,0.08), 0 0 22px color-mix(in srgb, var(--wt-cyan) 16%, transparent);
}
.news-grid-archive .news-card:nth-child(even) {
	border-color: color-mix(in srgb, var(--wt-purple) 40%, transparent);
	box-shadow: 0 1px 3px rgba(28,44,104,0.08), 0 0 22px color-mix(in srgb, var(--wt-purple) 16%, transparent);
}
.news-grid-archive .news-card:nth-child(odd):hover {
	border-color: color-mix(in srgb, var(--wt-cyan) 66%, transparent);
	box-shadow: var(--wt-shadow-lg), 0 0 34px color-mix(in srgb, var(--wt-cyan) 36%, transparent);
}
.news-grid-archive .news-card:nth-child(even):hover {
	border-color: color-mix(in srgb, var(--wt-purple) 66%, transparent);
	box-shadow: var(--wt-shadow-lg), 0 0 34px color-mix(in srgb, var(--wt-purple) 36%, transparent);
}

/* News/archive section head (27 Aug 2026) — a small eyebrow+title row
   above the card grid, reusing the Plans pages' .band-head/.band-eyebrow/
   .band-title typography (see the "Broadband Plans v2" block further down
   this file) for consistency with the rest of the Navy-Dominant pass,
   rather than the homepage's differently-fonted .section-head. Previously
   the grid sat directly under the banner with no heading of its own. */
.news-archive-head { margin-bottom: 2.4rem; }

/* ---------- CTA band ---------- */
.cta-band { background: var(--wt-navy); color: #fff; position: relative; overflow: hidden; }
/* Round 27 (14 July) — the purple/cyan glow recoloured to navy-family
   tones instead (was the last spot on the page still using the old
   brand gradient's colours); keeps the ambient depth without the
   cyan-blue-purple blend. */
.cta-band::before {
	content: ""; position: absolute; inset: 0;
	background: radial-gradient(700px 300px at 85% 120%, rgba(61,79,150,0.45), transparent 60%), radial-gradient(600px 280px at 8% -20%, rgba(28,44,104,0.4), transparent 60%);
}
/* Inner-page CTA bands (e.g. Support) get --wt-navy-grad instead of flat
   navy (16 July, Soana's request for gradient "navy parts") — scoped to
   .page-content-template only so the homepage's own CTA band, already
   finalized this session under Round 27's flat-navy direction, is left
   untouched. */
.page-content-template .cta-band { background: var(--wt-navy-grad); }
.cta-inner { position: relative; text-align: center; padding: 88px 22px; max-width: 760px; }
/* Small pill badge above the CTA heading — added 16 July, optional per-block
   (see wantok_block_cta's 'badge' key). */
.cta-badge {
	display: inline-flex; align-items: center; gap: 0.4rem;
	background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.25);
	color: #fff; font-size: 0.82rem; font-weight: 600; letter-spacing: 0.02em;
	padding: 0.42em 1em; border-radius: 999px; margin-bottom: 1.1rem;
}
body.wp-theme-wantok-new .cta-title { color: #fff; font-size: clamp(2rem, 4.4vw, 3rem); letter-spacing: -0.015em; }
.cta-lead { color: var(--wt-on-dark-2); font-size: 1.1rem; margin-bottom: 1.6rem; }
.cta-actions { display: flex; gap: 0.8rem; justify-content: center; flex-wrap: wrap; }

/* ---------- Contact details (plain icon list) ----------
   Added 16 July for the Support page's contact-details section; reworked
   twice same day per client feedback: first an earlier, more colourful
   card-based version ("icons all over the place", didn't like the cards),
   then the icon moved above the text instead of beside it (also fixes the
   detail line only having width-minus-icon to work with, which was
   wrapping phone numbers awkwardly) — icons are stroke-only navy with no
   background shape, items have no card chrome, and Phone/Email sit in a
   2-up row with Office/location spanning the full width below (see
   wantok_block_contact_grid()). */
.contact-grid-lead { color: var(--wt-ink-2); max-width: 620px; margin: -0.4rem 0 1.6rem; }
/* 'contact-section' (added 16 July, see wantok_block_contact_grid()) drops
   the usual 900px text-column cap so this can use the container's full
   1180px, and adds extra vertical padding — the default .richtext-section
   sizing read as too small/narrow for a two-column list+map layout versus
   a column of body text. Icon/type sizes below were bumped to match. */
.contact-section { max-width: 1180px; padding-top: 56px; padding-bottom: 56px; }
/* align-content: center (16 July, after the closing CTA band was removed and
   the map's min-height was trimmed back down — see .contact-split-map below)
   so if the list still ends up shorter than the stretched row, the leftover
   space is split evenly above/below the three items instead of only
   appearing as a gap underneath — keeps the list optically centered against
   the map rather than looking top-anchored and unbalanced. */
.contact-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2.4rem 2.8rem; align-content: center; }
.contact-item { display: flex; flex-direction: column; align-items: flex-start; }
.contact-item--full { grid-column: 1 / -1; }
.contact-item-icon { display: inline-flex; color: var(--wt-navy); margin-bottom: 0.7rem; }
.contact-item-title { font-family: var(--wt-font-display); font-weight: 700; font-size: 1.08rem; color: var(--wt-ink); margin: 0 0 0.3rem; }
.contact-item-detail { color: var(--wt-ink-2); margin: 0; font-size: 1.05rem; line-height: 1.5; }
/* Phone/Email details are real tel:/mailto: links now (16 July) — styled to
   read the same as plain text (no default blue/underline) so making them
   clickable doesn't change the section's look, just its behaviour; purple
   on hover matches the site's other inline-link hover treatment. */
.contact-item-detail a { color: inherit; text-decoration: none; }
.contact-item-detail a:hover { color: var(--wt-purple); text-decoration: underline; }
/* Dashed dividers (added 16 July — the plain list read a little bare)
   between items, echoing the dashed-border treatment already used
   elsewhere on the site (e.g. .content-todo-note) instead of a new style.
   Assumes the common "2-up row + full-width row below" pattern: a vertical
   dash between the two top items, a horizontal one above the full row. */
/* Scoped to .contact-grid (16 July, added alongside the overlay layout
   variant below) — these were previously bare .contact-item selectors,
   which would otherwise leak into .contact-overlay-list's plain vertical
   stack (that layout has its own, simpler divider rule instead). */
.contact-grid .contact-item:nth-child(2):not(.contact-item--full) { border-left: 1px dashed var(--wt-border); padding-left: 2rem; }
.contact-grid .contact-item--full { border-top: 1px dashed var(--wt-border); padding-top: 1.8rem; margin-top: 0.4rem; }
@media (max-width: 560px) {
	.contact-grid { grid-template-columns: 1fr; }
	.contact-grid .contact-item--full { grid-column: auto; }
	.contact-grid .contact-item:nth-child(2):not(.contact-item--full) {
		border-left: 0; padding-left: 0;
		border-top: 1px dashed var(--wt-border); padding-top: 1.8rem; margin-top: 0.4rem;
	}
}

/* Contact grid paired with a map (added 16 July, per a second reference the
   client liked): the icon list becomes a box on the left, map on the
   right. Both are grid items in the same row, so they stretch to match
   each other's height with no fixed height needed on either side. Left
   column widened same day (client didn't mind the contact info taking
   more room) so detail lines have breathing room now that icons sit above
   the text rather than eating into the row's width.
   min-height re-tuned (16 July, later same day, after the closing CTA band
   was removed): the original 400px was set to give the map "a substantial
   presence even if the list ends up short," but against the 3-item list's
   real content height that read as an oversized, mostly-empty map box —
   dropped to 320px, closer to the list's natural height, so the two panels
   read as sized to match each other rather than the map dwarfing the list.
   See .contact-grid's align-content: center above for how any remaining
   slack is shared instead of just sitting at the bottom. */
.contact-split { display: grid; grid-template-columns: minmax(300px, 1.15fr) minmax(280px, 1fr); gap: 2rem; align-items: stretch; }
.contact-split-map { min-height: 320px; }
.contact-split-map iframe { display: block; width: 100%; height: 100%; border: 0; }
@media (max-width: 780px) {
	.contact-split { grid-template-columns: 1fr; }
	.contact-split-map { min-height: 260px; }
}
/* Location map embed (16 July) — see wantok_block_map(). */
.map-embed { position: relative; border-radius: var(--wt-radius); overflow: hidden; box-shadow: var(--wt-shadow); border: 1px solid var(--wt-border); }
/* Transparent full-cover link (16 July) so clicking anywhere on the map
   opens real Google Maps in a new tab — a keyless embed iframe on its own
   never does this. Sits above the iframe (z-index) and captures every
   click itself, which does mean the mini embedded map can no longer be
   panned/zoomed in place; that trade-off is intentional here since the
   embed is informational, not a working mini-map someone needs to explore. */
.map-embed-link { position: absolute; inset: 0; z-index: 1; }
.map-embed-badge {
	position: absolute; right: 16px; bottom: 16px;
	display: inline-flex; align-items: center; gap: 0.4rem;
	background: var(--wt-navy); color: #fff; font-family: var(--wt-font-display);
	font-size: 0.86rem; font-weight: 600; padding: 0.55em 1.1em;
	border-radius: 999px; box-shadow: var(--wt-shadow-lg);
}
.map-embed-link:hover .map-embed-badge { background: var(--wt-purple); }
.map-embed iframe { display: block; }

/* Contact "overlay" layout — a /frontend-design alternative to .contact-split
   (16 July, exploration only, not yet the live layout): the map becomes a
   tall full-width backdrop instead of one half of a two-column row, and the
   contact list sits in a solid card floating over its corner, like a
   "visit us" section on a hospitality site. Card is absolutely positioned
   only from tablet width up, where there's enough map width for it not to
   cover the whole thing — below that it drops to a plain stacked block
   under the map instead of overlapping, since there's no room to overlap
   without hiding most of the map.
   Card anchored top-left rather than bottom-left (16 July, revised same
   day) — bottom-left is where Google's own map puts its attribution/terms
   link, and bottom-right is where its zoom controls sit, so a card pinned
   to either bottom corner fights with that chrome. Anchoring from the top
   leaves the whole bottom strip clear regardless of the card's actual
   height. Map height bumped 460px → 520px to give the card real breathing
   room above that bottom strip, and the card gets a max-height + scroll as
   a safety net in case its content ever runs longer than expected. */
.contact-overlay { position: relative; }
/* height (not min-height) is required here — the iframe below uses
   height: 100%, and a percentage height only resolves against a parent
   with a *definite* height; min-height alone doesn't count. Without this,
   the iframe collapses to the browser's default intrinsic iframe size and
   Google's own map canvas inside it initializes at that wrong size, then
   gets stretched to fill the box — which is what read as "distorted". */
.contact-overlay-map { height: 520px; }
.contact-overlay-map iframe { display: block; width: 100%; height: 100%; border: 0; }
.contact-overlay-card {
	background: #fff; border-radius: var(--wt-radius); border: 1px solid var(--wt-border);
	box-shadow: var(--wt-shadow-lg); padding: 1.7rem 1.9rem; margin-top: 1.4rem;
}
.contact-overlay-list { display: flex; flex-direction: column; gap: 1.05rem; margin-bottom: 1.3rem; }
.contact-overlay-list .contact-item:not(:first-child) { border-top: 1px dashed var(--wt-border); padding-top: 1.05rem; }
.contact-overlay-directions { width: 100%; justify-content: center; }
@media (min-width: 640px) {
	.contact-overlay-card {
		position: absolute; left: 32px; top: 32px; bottom: auto; margin-top: 0;
		width: min(360px, calc(100% - 64px));
		max-height: calc(100% - 64px); overflow-y: auto;
	}
}
@media (max-width: 560px) {
	.contact-overlay-map { height: 340px; }
}

/* Contact "columns" layout — a third /frontend-design alternative (16 July),
   per a reference design Soana liked: three equal, centred columns (Phone /
   Address / Email) with thin divider lines between them, no paired map (the
   map moved to its own full-bleed block below — see .map-embed-full-bleed).
   Reuses .contact-item, but centred instead of the default left-aligned
   layout, so it's scoped under .contact-columns rather than changing the
   shared .contact-item rules other layouts still depend on. */
.contact-columns { display: grid; grid-template-columns: repeat(3, 1fr); }
.contact-columns .contact-item { align-items: center; text-align: center; padding: 0 1.8rem; }
.contact-columns .contact-item-detail { max-width: 260px; }
.contact-columns .contact-item:not(:first-child) { border-left: 1px solid var(--wt-border); }
@media (max-width: 720px) {
	.contact-columns { grid-template-columns: 1fr; gap: 2rem; }
	.contact-columns .contact-item { padding: 0; }
	.contact-columns .contact-item:not(:first-child) {
		border-left: 0; border-top: 1px solid var(--wt-border);
		padding-top: 1.8rem; margin-top: 0.2rem;
	}
}

/* Support page split view — contact list card + inline "Send us a message"
   form card (27 Aug 2026, Navy-Dominant pass — see
   wantok_block_contact_form_split() in page-content-render.php). Both
   sides reuse .richtext-section--compact (the Anker Solix product-card
   box) plus .glow-cyan/.glow-purple for the coloured-ring-glow accent —
   no new card chrome needed, just this grid to hold the two side by side.
   Equal-width columns + align-items: stretch (27 Aug, later same day —
   Soana asked for the two boxes "even and balanced": the initial 0.85fr/
   1.15fr split plus align-items: start left the shorter 3-item contact
   card visibly shorter than the 4-field form card, with the columns
   themselves uneven widths too). Both now match each other's width AND
   height; .contact-form-info below spreads its content to fill that
   matched height instead of leaving a dead gap under the social row. */
.contact-form-split { display: grid; grid-template-columns: minmax(300px, 1fr) minmax(300px, 1fr); gap: 1.8rem; align-items: stretch; margin-top: 1.6rem; }
.contact-form-split > .richtext-section--compact { margin: 0; }
@media (max-width: 780px) {
	.contact-form-split { grid-template-columns: 1fr; }
}
/* Stretched to the form card's full height by .contact-form-split above —
   without this flex, the shorter item list + social row would just sit at
   the top of that extra height, leaving a dead gap at the bottom. Spacing
   the two apart instead makes the taller card read as deliberately laid
   out, not padded out to match. */
.contact-form-info { display: flex; flex-direction: column; justify-content: space-between; }
/* Left-aligned variant of .connect-block (which is centred by default, for
   its other use directly under .contact-columns) — inside the narrower
   left-hand card here, a centred block reads oddly next to the
   left-aligned contact-item list above it. */
.connect-block--left { text-align: left; }
.connect-block--left .connect-social { justify-content: flex-start; }
.contact-form-info .contact-overlay-list { margin-bottom: 0; }
.contact-form-title { font-family: var(--wt-font-display); font-weight: 700; font-size: 1.2rem; margin: 0 0 6px; color: var(--wt-ink); }
.contact-form-sub { font-size: 0.92rem; color: var(--wt-ink-2); margin: 0 0 20px; line-height: 1.5; }

/* "Connect with us" social row (16 July) — sits right after .contact-columns,
   before the map. Reuses the same real social URLs as the header's
   .utility-social (via wantok_opt()) rather than duplicating them, just
   scaled up and recoloured for a white section background instead of the
   navy utility bar. */
.connect-block { text-align: center; margin-top: 2.6rem; }
.connect-label {
	font-family: var(--wt-font-display); font-weight: 700; font-size: 1rem;
	color: var(--wt-ink); text-transform: uppercase; letter-spacing: 0.04em;
	margin-bottom: 1rem;
}
.connect-social { display: flex; gap: 0.7rem; justify-content: center; list-style: none; }
.connect-social a {
	display: inline-grid; place-items: center; width: 42px; height: 42px;
	border-radius: var(--wt-radius); background: var(--wt-navy); color: #fff;
}
.connect-social a:hover { background: var(--wt-purple); }
/* 28 August 2026 — same real-brand-icon swap as .utility-social above
   (see that comment + wantok_social_icon_svg() in functions.php), just
   sized up to fill this larger 42px square instead of the header's
   smaller one. */
.connect-social .social-ico { width: 20px; height: 20px; display: block; }

/* Full-bleed wide/short map (16 July) — replaces the tall paired map on the
   Support page; see wantok_block_map()'s 'full_bleed' option. Overrides
   .map-embed's rounded corners/border/shadow since those read oddly on a
   section spanning the full viewport edge-to-edge — same reasoning as
   .page-title-banner just above going edge-to-edge with no card chrome.
   height (not min-height): same reason as .contact-overlay-map earlier —
   the iframe's height:100% needs a definite parent height to resolve. */
.map-full-bleed-section { width: 100%; }
.map-embed-full-bleed { border-radius: 0; border: 0; box-shadow: none; height: 340px; }
.map-embed-full-bleed iframe { display: block; width: 100%; height: 100%; border: 0; }
@media (max-width: 560px) {
	.map-embed-full-bleed { height: 260px; }
}
/* Heading + office address sitting above the full-bleed map (17 July —
   the address moved here from the contact-grid columns, which now show
   Office Hours instead; see wantok_block_map()'s 'address' handling).
   .map-full-bleed-section itself is edge-to-edge (no .container), so this
   intro block brings its own .container class for the max-width/centring/
   padding, same trick as .page-title-banner-mascot-wrap. h2 styling
   matches .richtext-section h2's underline-accent treatment for
   consistency with the rest of the page, since this sits outside any
   .richtext-section itself. */
.map-full-bleed-intro { padding-bottom: 1.6rem; }
.map-full-bleed-intro h2 {
	position: relative; padding-bottom: 0.7rem; margin: 0 0 0.6rem; font-size: 1.5rem;
}
.map-full-bleed-intro h2::after {
	content: ""; position: absolute; left: 0; bottom: 0; width: 56px; height: 3px; border-radius: 2px; background: var(--wt-purple);
}
.map-address { color: var(--wt-ink-2); font-size: 1.03rem; margin: 0; max-width: 520px; }

/* Generic hairline section divider (30 August 2026) — for a plain
   `array( 'type' => 'divider' )` block (wantok_block_divider() in
   page-content-render.php), used where two stacked sections would
   otherwise just be whitespace apart with no visible edge between them.
   First use: Support page's contact section / map section boundary. Same
   hairline weight/colour as .pc-kits-divider elsewhere, but page-agnostic.
   No top margin — the section above already carries its own bottom
   padding, so this just needs to sit inside that existing gap rather than
   adding a second one on top of it; a bit of bottom margin keeps it from
   sitting flush against whatever follows (e.g. the full-bleed map, which
   has no top spacing of its own). */
.wt-section-divider {
	border: 0; border-top: 1px solid var(--wt-border);
	max-width: var(--wt-maxw); margin: 0 auto 40px;
}

/* ---------- Footer ---------- */
.site-footer { background: var(--wt-navy-2); color: var(--wt-on-dark); }
/* 17 July: was a 4-track grid (Brand/Explore/Contact/Widgets); the Explore
   column was removed from footer.php (see its comment there), leaving
   just Brand + "Talk to us" by default. A percentage-based grid split
   (e.g. 1.6fr/1fr) looked worse than the original 4-column version once
   only two, content-sized blocks remained — each column stretched across
   its full share of the 1180px container while the actual text inside
   stayed left-aligned and narrow, leaving an odd dead gap in the middle
   *and* more dead space past "Talk to us" out to the container's right
   edge. Switched to flex + space-between instead: Brand anchors to the
   container's left edge, "Talk to us" anchors to its right edge, both
   sized to their own content — the classic symmetric "bookend" footer
   layout, which reads as intentional at any column count instead of
   assuming exactly 3-4 evenly-sized blocks. */
/* ---------- Glow Contact Panel (30 Aug 2026) ----------
   Sitewide footer CTA — replaces the homepage-only .cta-band duplicate
   (see front-page.php). Deliberately prefixed footer-glow-* rather than
   reusing the sitewide .glow-cyan/.glow-purple "ring glow" convention
   used on cards elsewhere in this file, so the two never collide. Uses
   the same gradient-border technique as the card glows (a solid inner
   background painted to the padding-box, the brand gradient painted to
   the border-box) but as its own scoped rule. */
.footer-glow-wrap { padding: 64px 22px 0; }
.footer-glow-panel {
	border-radius: 22px; padding: 38px 40px; display: flex;
	justify-content: space-between; align-items: center; gap: 2rem; flex-wrap: wrap;
	background: linear-gradient(var(--wt-navy-2), var(--wt-navy-2)) padding-box, var(--wt-grad) border-box;
	border: 1px solid transparent;
	box-shadow: 0 0 60px rgba(19,182,230,0.14), 0 0 90px rgba(122,57,187,0.1);
}
/* 30 August 2026 — bare `.footer-glow-title { color: #fff }` was losing to
   the sitewide `body.wp-theme-wantok-new h1,h2,h3,h4 { color: var(--wt-ink) }`
   rule on specificity (that selector is a class + element vs. this one's
   class alone) — same recurring trap as .pc-name/.hiw-step-title/.page-title
   elsewhere in this file. Soana flagged the panel's headline as unreadable
   (dark ink on the dark navy glow panel); needs the two-class form to win. */
body.wp-theme-wantok-new .footer-glow-title { font-family: var(--wt-font-display); font-size: 1.5rem; color: #fff; margin: 0 0 6px; }
.footer-glow-lead { margin: 0; color: var(--wt-on-dark-2); font-size: 0.95rem; }
.footer-glow-cta { display: flex; align-items: center; gap: 26px; flex-wrap: wrap; }
.footer-glow-direct { text-align: right; }
.footer-glow-num { display: block; font-family: var(--wt-font-display); font-size: 1.15rem; font-weight: 600; color: #fff; }
.footer-glow-num:hover { color: var(--wt-cyan); }
.footer-glow-mail { display: block; font-size: 0.88rem; color: var(--wt-on-dark-2); margin-top: 2px; }
.footer-glow-mail:hover { color: var(--wt-cyan); }

/* ---------- Balanced, centred brand stack ----------
   Replaces the old two-column .footer-grid (Brand | Talk to us) — see
   footer.php's 30 Aug rewrite. Reuses the .footer-social/.footer-address/
   .footer-logo class names since they're still the same elements, just
   restyled into a single centred column per the H&M-style reference
   Soana shared. */
.footer-stack { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 26px; padding: 56px 22px 50px; }
.footer-logo { display: inline-block; margin: 0; }
/* Enlarged from the old 32px squares to 44px circles, and given room of
   their own on a full row instead of sharing a column with the address
   (Soana: "enlarge the social media icons"). */
.footer-social { display: flex; gap: 14px; list-style: none; margin: 0; padding: 0; justify-content: center; }
.footer-social a {
	display: inline-grid; place-items: center; width: 44px; height: 44px;
	border-radius: 50%; background: rgba(255,255,255,0.07); color: var(--wt-on-dark);
}
.footer-social a:hover { background: var(--wt-cyan); color: var(--wt-navy-2); }
.footer-social .social-ico { width: 20px; height: 20px; display: block; }
.footer-address {
	font-style: normal; color: var(--wt-on-dark-2); font-size: 0.92rem;
	max-width: 640px; margin: 0; line-height: 1.6;
	text-wrap: balance; /* keeps the address to a balanced 1-2 lines at any width without a fixed manual break */
}
body.wp-theme-wantok-new .footer-heading { font-size: 1rem; color: #fff; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 1rem; }
.footer-menu { list-style: none; display: grid; gap: 0.6rem; }
.footer-menu a { color: var(--wt-on-dark-2); font-size: 0.96rem; }
.footer-menu a:hover { color: var(--wt-cyan); }
.footer-bottom { border-top: 1px solid rgba(255,255,255,0.1); }
.footer-bottom-inner { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 4px; padding: 20px 22px; font-size: 0.85rem; }
.copyright, .footer-microcopy { margin: 0; font-size: 0.86rem; color: var(--wt-on-dark-2); }

/* ---------- Blog / pages (inner templates) ---------- */
.page-header { margin-bottom: 2rem; }
.page-title { font-size: clamp(1.8rem, 4vw, 2.6rem); }
.entry-content { font-size: 1.05rem; }
.entry-content h2 { font-size: 1.6rem; margin-top: 1.6em; }
.entry-content h3 { font-size: 1.3rem; margin-top: 1.4em; }
.entry-content img, .page-thumb img, .entry-thumb img { border-radius: var(--wt-radius); }
.entry-meta { color: var(--wt-ink-3); font-size: 0.9rem; margin-bottom: 0.6rem; display: flex; gap: 0.8rem; flex-wrap: wrap; }
.post-list { display: grid; gap: 1.6rem; }
.post-card { display: grid; grid-template-columns: 280px 1fr; gap: 1.4rem; background: var(--wt-bg); border: 1px solid var(--wt-border); border-radius: var(--wt-radius); overflow: hidden; box-shadow: var(--wt-shadow); }
.post-card-thumb img { width: 100%; height: 100%; object-fit: cover; }
.post-card-body { padding: 22px 24px; }
.entry-title a { color: var(--wt-ink); }
.entry-title a:hover { color: var(--wt-blue); }
.read-more { font-weight: 600; font-family: var(--wt-font-display); }
.post-navigation { display: flex; justify-content: space-between; gap: 1rem; margin-top: 2.5rem; flex-wrap: wrap; }
.post-navigation a { font-weight: 600; }

/* Pagination */
.pagination { margin-top: 2.5rem; display: flex; }
.pagination .nav-links { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.pagination .page-numbers { display: inline-grid; place-items: center; min-width: 42px; height: 42px; padding: 0 10px; border: 1px solid var(--wt-border); border-radius: 10px; color: var(--wt-ink); font-weight: 600; }
.pagination .page-numbers.current { background: var(--wt-grad); color: #fff; border-color: transparent; }
.pagination a.page-numbers:hover { border-color: var(--wt-blue); color: var(--wt-blue); }

/* Search form */
.search-form { display: flex; gap: 0.5rem; max-width: 520px; margin-top: 1rem; }
.search-field { flex: 1; padding: 0.7em 1em; border: 1px solid var(--wt-border); border-radius: 999px; font: inherit; }

/* 404 */
.error-404-area { text-align: center; max-width: 640px; }
body.wp-theme-wantok-new .error-code { font-family: var(--wt-font-display); font-weight: 700; font-size: clamp(4rem, 14vw, 8rem); line-height: 1; background: var(--wt-grad); -webkit-background-clip: text; background-clip: text; color: transparent; margin: 0; }
.error-links { display: flex; gap: 1rem; justify-content: center; list-style: none; flex-wrap: wrap; margin: 1rem 0 1.6rem; }
.error-links a { font-weight: 600; }

/* ---------- Inner content pages (page-content.php — replaces Elementor) ---------- */
.page-content-template.content-area { padding-top: 0; }
/* .content-area's 64px bottom padding is meant as breathing room after a
   normal (light-background) closing block. When a page's last block is a
   full-bleed navy .cta-band instead (e.g. Support's new closing CTA, 16
   July), that padding shows through as an unwanted white seam between the
   navy band and the navy footer. Pull the band down to close the gap —
   scoped to :last-child so pages that end on a regular block keep their
   normal spacing before the footer. */
.page-content-template .cta-band:last-child { margin-bottom: -64px; }
/* Same white-seam fix as .cta-band above, for the Support page's new
   full-bleed map when it's the last block before the footer (16 July). */
.page-content-template .map-full-bleed-section:last-child { margin-bottom: -64px; }
/* Same white-seam fix again, for the News page's tinted card-grid band
   (16 July) — it's the last (and only) block in index.php/archive.php's
   <main>, so .content-area's 64px bottom padding was leaving a plain white
   gap between the --wt-surface tint and the navy footer, which read as the
   tint "not going all the way through" even though it already ran full
   viewport width. */
.page-content-template .news-archive:last-child { margin-bottom: -64px; }

/* Full-bleed masthead banner — breaks out of the container to span the
   viewport edge-to-edge like the production site, with a wave divider into
   the white content below. Switched from the old cyan-blue-purple gradient
   to solid navy (16 July) to match the homepage's redesigned brand language,
   then to --wt-navy-grad later the same day per Soana's request to give the
   "navy parts" some gradient depth back (kept in the navy family, not the
   brighter old brand gradient — see --wt-navy-grad's own comment). The
   circuit-line texture and large translucent icon watermark that used to
   sit here were also removed the same day (busy against the navy). */
.page-title-banner {
	position: relative; overflow: hidden;
	background: var(--wt-navy-grad); color: #fff;
	padding: 56px 0 46px;
}
.page-title-banner-inner { position: relative; z-index: 1; max-width: 700px; }
.page-title-banner .section-eyebrow { color: rgba(255,255,255,0.78); }
.page-title-banner .section-eyebrow::before { background: rgba(255,255,255,0.55); }
.page-content-template .page-title-banner .page-title {
	color: #fff; text-transform: uppercase; letter-spacing: 0.02em;
	font-size: clamp(1.7rem, 3.8vw, 2.5rem); margin-bottom: 0.6rem;
}
/* 30 August 2026 — banner entrance animation ("Shimmer sweep", picked by
   Soana over 7 other options after a /frontend-design review — see
   wantok_banner_entrance_animation_30aug.md). Every inner page's banner
   eyebrow + H1 previously just appeared instantly with the rest of the
   page; this gives them a one-time on-load rise (matching the same
   blur+scale+rise language as the sitewide .reveal-init system below,
   just triggered on load instead of on scroll, since banner text is
   always in the initial viewport) plus a single soft cyan light sweep
   across the headline once it settles, reusing the same cyan accent
   already used for the Anker SOLIX product-card glow rather than
   inventing a new brand color. Gated on .wt-js (added by theme.js only
   when JS runs AND prefers-reduced-motion isn't set — see the "Reveal
   animation" section far below), so no-JS and reduced-motion visitors
   simply see the banner text in place with no hidden state, exactly
   like every other .reveal-init use on this site. Deliberately NOT
   split per-word (unlike a couple of the other 7 options Soana saw) —
   titles here range from "Solar Energy" to "AI - The New Way of Doing
   Things", and a whole-block rise is the only treatment that looks
   identical regardless of title length. Scoped to .page-title-banner-
   inner so it applies to every banner variant (plain navy, photo hero,
   video hero, hub-viewer, mascot) without touching the separate
   .page-centered-header treatment used only on Services and Plans. */
.wt-js .page-title-banner-inner .section-eyebrow,
.wt-js .page-title-banner-inner .page-title {
	opacity: 0; transform: translateY(20px) scale(0.97); filter: blur(4px);
}
.wt-js .page-title-banner-inner .section-eyebrow {
	animation: wt-banner-rise 0.7s cubic-bezier(0.16,1,0.3,1) forwards;
}
.wt-js .page-content-template .page-title-banner .page-title {
	animation: wt-banner-rise 0.8s cubic-bezier(0.16,1,0.3,1) 0.15s forwards,
	           wt-banner-sheen 1.3s ease-out 1.05s forwards;
	background: linear-gradient(100deg, #fff 40%, #fff 46%, var(--wt-cyan) 50%, #fff 54%, #fff 100%);
	background-size: 260% 100%;
	background-position: 150% 0;
	-webkit-background-clip: text; background-clip: text; color: transparent;
}
@keyframes wt-banner-rise { to { opacity: 1; transform: none; filter: blur(0); } }
@keyframes wt-banner-sheen { from { background-position: 150% 0; } to { background-position: -40% 0; } }
.page-title-banner .page-quote { color: rgba(255,255,255,0.92); }
.page-title-banner .page-intro { color: rgba(255,255,255,0.9); margin: 0; }
.page-title-banner-wave {
	position: absolute; left: 0; bottom: -1px; width: 100%; height: 34px; display: block;
}
/* 10 August — video stacked below the eyebrow/title/quote, still inside
   the navy banner (Broadband Plans / Starlink Plans only — see
   wantok_banner_video() in page-content-render.php). Sits outside
   .page-title-banner-inner's 700px text cap so it can use the full
   .container width, but is itself capped narrower than that (760px) and
   centred, so it reads as a deliberate focal element rather than a
   full-bleed strip. .video-frame's own navy fill (real footage) blends
   into the banner with no visible seam; the placeholder state gets its
   own light-on-navy treatment below since the default placeholder
   (light-grey surface + dark text) would otherwise be illegible here. */
.page-title-banner-video-wrap { position: relative; z-index: 1; margin-top: 2rem; }
/* Two-class selector (matches .page-title-banner .page-quote's pattern
   just above) needed to outrank body.wp-theme-wantok-new h1/h2/h3/h4's
   color:var(--wt-ink) rule near the top of this file — a plain single-class
   rule loses that specificity fight and the heading rendered near-invisible
   dark navy text on the navy banner background. */
.page-title-banner .page-title-banner-video-heading {
	color: #fff; text-align: center; font-size: 1.15rem; margin: 0 0 1rem;
}
.video-frame--banner { max-width: 760px; margin: 0 auto; }
.video-frame--banner.video-frame--placeholder {
	background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.4);
}
.video-frame--banner .video-placeholder-inner { color: rgba(255,255,255,0.85); }
.video-caption--banner { color: rgba(255,255,255,0.75); text-align: center; }
@media (max-width: 700px) {
	.page-title-banner-video-wrap { margin-top: 1.4rem; }
}

/* ---------- Video-as-banner hero variant ----------
   10 August, later same day — Soana: "I want the gif/video to be the
   banner... like the hero banner on the home page", not a boxed frame
   sitting inside it (the rules just above this comment). Mirrors
   front-page.php's .hero/.hero-bg/.hero-carousel/.hero-scrim three-layer
   pattern: a full-bleed background layer, a dark scrim above it for text
   contrast against arbitrary footage, and the existing eyebrow/title/
   quote column floating on top via z-index — just without the
   multi-slide carousel machinery this single-video banner doesn't need.
   Rolling out to Broadband Plans first via the 'banner_video_hero' data
   key/wantok_banner_video_bg() — see that function's own comment — so
   this variant only applies where that key is set; every other
   "...-plans" page keeps the boxed .page-title-banner-video-wrap
   treatment above untouched for now. */
.page-title-banner--video-hero {
	/* 10 August, later same day — bumped from min(58vh, 520px): Soana
	   would rather grow the banner box than resize/re-export the clips.
	   Since the crop is purely a function of this box's aspect ratio
	   versus each clip's own (see wantok_banner_video_bg() in
	   page-content-render.php), a taller box narrows that gap and shows
	   more of every clip for free — Broadband's ~21:9 clip is now close
	   to zero crop, and the four ~16:9 clips lose proportionally less
	   top/bottom than before. Kept the min()/vh pairing so short viewports
	   still shrink gracefully instead of overflowing the screen. */
	min-height: min(65vh, 640px);
	display: flex;
	align-items: center;
	padding: 0;
}
.page-title-banner-video-bg {
	position: absolute; inset: 0; z-index: 0; overflow: hidden;
	background: var(--wt-navy-grad);
}
.page-title-banner-video-bg video {
	position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
/* 10 August, later same day — tried a letterboxed/blurred-backdrop
   treatment here for clips whose native aspect ratio doesn't match this
   banner (see wantok_banner_video_bg() in page-content-render.php for
   the full story) — Soana wants the plain full-bleed cover crop kept as
   is instead, just aimed at the right part of the frame per clip via an
   inline object-position style set from that function's 'position' key.
   Nothing extra needed here as a result. */
/* Deliberately NOT centred in the middle of the banner the way the boxed
   variant's placeholder is — this banner is full-bleed behind the
   eyebrow/title/quote (which sit centred-ish on the left via flex on
   .page-title-banner--video-hero), so a dead-centre placeholder message
   landed directly on top of the headline text (caught via screenshot —
   the play icon sat right over "BROADBAND PLANS"). Tucked into the
   bottom-right corner instead, small and quiet, like a watermark/status
   note rather than a competing focal point — this is only relevant pre-
   footage anyway; once a real 'file' is set this whole placeholder branch
   stops rendering. */
.page-title-banner-video-bg--placeholder .video-placeholder-inner {
	position: absolute; right: 28px; bottom: 26px;
	display: flex; align-items: center; gap: 0.6rem;
	text-align: left; color: rgba(255,255,255,0.65);
}
.page-title-banner-video-bg--placeholder .video-placeholder-icon {
	width: 30px; height: 30px; font-size: 0.68rem; margin: 0; flex-shrink: 0;
}
.page-title-banner-video-bg--placeholder .video-placeholder-inner p { margin: 0; font-size: 0.82rem; }
@media (max-width: 700px) {
	.page-title-banner-video-bg--placeholder .video-placeholder-inner { right: 18px; bottom: 40px; }
}
.page-title-banner-scrim {
	position: absolute; inset: 0; z-index: 1; pointer-events: none;
	/* Flat, not directional like the homepage's carousel-matching scrim —
	   there's no fixed subject/composition to blend around here, just an
	   unknown future clip, so a uniform dark veil is the safest way to
	   guarantee the white eyebrow/title/quote stay legible regardless of
	   what the footage looks like. */
	background: rgba(6, 11, 28, 0.58);
}
.page-title-banner--video-hero .page-title-banner-inner { z-index: 2; }
/* Keeps the white wave transition crisp on top of the scrim/video instead
   of being dulled by the scrim sitting over it (both would otherwise land
   in the same default stacking bucket as the video-bg layer, painted in
   DOM order — scrim would end up on top since it comes later, muddying
   the wave's white against whatever's directly behind it). */
.page-title-banner--video-hero .page-title-banner-wave { z-index: 3; }
@media (max-width: 700px) {
	.page-title-banner--video-hero { min-height: min(68vh, 480px); }
}

/* The wave's fill is hardcoded to #fff in the markup on the assumption the
   banner always sits above a plain white block — true for Support/About Us,
   but the News page (16 July) sits above a --wt-surface-tinted section
   instead, so the plain-white wave read as its own white sliver/gap between
   the navy banner and the tint. This class overrides the fill to match. */
.page-title-banner-wave--surface path { fill: var(--wt-surface); }
/* Mascot banner variant (16 July) — Soana provided a character render
   ("contact page tokobot.png") to sit in the page banner: a boy mascot
   already rendered on a solid navy background that's an almost exact match
   for --wt-navy (#0b1330 vs #0c1330 — imperceptible), and already composed
   with the character anchored to the right and empty navy space on the
   left. That match means the image can just sit on top of the banner's own
   navy fill with no visible seam or box — no cropping or background-color
   trickery needed, the "empty" two-thirds of the image reads as more banner
   background. Banner grows taller (min-height, tapered down at two
   breakpoints) to give the character real presence instead of a sliver;
   hidden below 700px instead of trying to make it coexist with the
   headline text in a narrow column — see .page-title-banner-mascot below. */
.page-title-banner--mascot { min-height: 400px; display: flex; align-items: center; }
/* 17 July fix — Soana flagged the mascot as "too small and too far to the
   right" on wide screens. Root cause: .page-title-banner-mascot was
   positioned with right:0 against the full-bleed banner, so on wide
   viewports it anchored to the literal browser edge — far outside the
   centred 1180px (--wt-maxw) text column — instead of sitting near the
   content. Fix: wrap the image in .page-title-banner-mascot-wrap, which
   reuses .container's max-width/centring/padding via an absolutely
   positioned inset:0 box (the inset:0 + max-width + margin:auto centering
   trick), so the mascot's right edge now tracks the same right edge as the
   headline column instead of the raw viewport edge.
   17 July, second pass — the first version of this fix also bumped the
   image to height:128%, which (bottom:0-anchored, so all the added height
   pushes the image upward) cropped the character's head off the top,
   clipped by .page-title-banner's overflow:hidden. Checked the source
   image's actual bounding box (contact-page-tokobot.png / support-mascot.jpg,
   1280x720): the character's head starts at y=55 (~7.6% down) and feet sit
   right at y=719 (~100%, already flush with the image's own bottom edge) —
   so there's very little headroom in the source frame itself to work with.
   Any height above ~108% pushes the head above the banner's top edge and
   off-screen. Fixed properly this time by growing the *banner* itself
   (min-height 340->400px, 260->300px at the 1000px breakpoint) so the
   character can be genuinely bigger via a taller banner, while keeping the
   image itself at a safe 105% (leaves ~3% clearance above the head, so it
   isn't clipped even accounting for sub-pixel rounding). Net effect vs. the
   original (pre-17-July) sizing: character renders ~24% larger with the
   whole head visible. */
.page-title-banner-mascot-wrap {
	position: absolute; inset: 0; margin: 0 auto; z-index: 0; pointer-events: none;
}
.page-title-banner-mascot {
	position: absolute; right: 6%; bottom: 0;
	height: 105%; width: auto; pointer-events: none;
}
@media (max-width: 1000px) {
	.page-title-banner--mascot { min-height: 300px; }
	.page-title-banner-mascot { right: 2%; height: 105%; }
}
@media (max-width: 700px) {
	.page-title-banner--mascot { min-height: auto; }
	.page-title-banner-mascot { display: none; }
}

/* About Us — photo hero banner (island collage + WANTOK wordmark), replacing
   the default gradient. The title/quote/intro text is kept in the markup
   for accessibility/SEO (see .screen-reader-text on .page-title-banner-inner
   below) but hidden visually so the photo shows through unobstructed — no
   dark overlay needed since there's no text to keep legible. The circuit
   texture + icon watermark are hidden for this variant since the photo
   already provides visual interest. */
.page-title-banner--photo {
	background: url('assets/images/about-banner.jpg') center/cover no-repeat;
	min-height: 260px; display: flex; align-items: center;
}
.page-quote { font-family: var(--wt-font-display); font-style: italic; font-size: 1.14rem; margin: 0 0 0.4rem; }
.page-intro { font-size: 1.08rem; color: var(--wt-ink-2); max-width: 760px; }

/* 4 August — plain centered eyebrow+title header, ported from
   services-plans-glasscard-mockup.html's .mock-services-head--centered /
   .mock-header-divider, for pages that opt out of the usual navy
   page-title-banner via the 'centered_header' flag in page-content-data.php
   (see page-content.php). Sits directly in the white page canvas instead of
   a full-bleed navy band — first used on the Services and Plans page. */
.page-centered-header { text-align: center; padding-top: 64px; }
.page-centered-header .section-eyebrow { justify-content: center; }
.page-centered-header .services-title { margin: 0; }
.page-centered-header-divider {
	display: block; width: 64px; height: 1px; margin: 0 auto 5.5rem;
	background: var(--wt-border);
}

/* Services and Plans reel-hero — the single combined highlight video,
   full-bleed, sitting ABOVE this page's eyebrow/title (see
   wantok_services_reel_hero() in page-content-render.php and the 'reel'
   key in page-content-data.php). Second pass, 11 August — first shipped
   as a contained rounded card below the plain header (see the removed
   .services-reel-section/.services-reel rules this replaces); Soana
   preferred the video up top with the header text floating on it instead.
   Mirrors .page-title-banner--video-hero's video-bg + scrim + wave
   structure, but .services-reel-hero-scrim is a top-to-bottom gradient
   (transparent to solid --wt-navy) rather than a flat veil, so the
   footage visually fades into the same navy the header text sits inside
   — this page has no navy banner box of its own, so the fade IS how it
   gets a navy band to match every other inner page's banner. */
.services-reel-hero {
	position: relative; overflow: hidden;
	/* 11 August, third pass — Soana asked for a smaller banner than the
	   62vh/560px first pass, and for the header text centred in it rather
	   than anchored to the bottom edge (align-items: flex-end -> center
	   below) — hugging the bottom put the text right against the wave's
	   curve, which read as off-balance rather than "centred". Centring the
	   text in a shorter box still clears the wave/showcase grid below (see
	   the .services-showcase-section padding-top bump further down this
	   file), so both requests are really the same fix. */
	min-height: min(46vh, 420px);
	display: flex; align-items: center;
	padding-bottom: 34px;
}
/* 11 August, fifth pass — matches .hero-overlay-page .hero's own pull
   (front-page.php/style.css) so this reel-hero runs up underneath the
   now-transparent sticky header (see header.php's is_page(
   'services-and-plans' ) check and page-content.php's matching
   'hero-overlay-page' class on <main>) instead of stopping short of it
   — the footage now visibly continues behind the nav row, the same way
   the homepage hero does, rather than the header sitting as a solid bar
   above a hard edge. 30 Aug 2026 — values re-measured to match the
   "Network Rail" flush-bar header (reverted the same day from the
   floating "Network Dock" card); see the matching comment on
   .hero-overlay-page .hero above. */
.hero-overlay-page .services-reel-hero { margin-top: -114px; }
@media (max-width: 572px) {
	.hero-overlay-page .services-reel-hero { margin-top: -142px; }
}
.services-reel-hero-bg {
	position: absolute; inset: 0; z-index: 0; background: var(--wt-navy-grad);
}
.services-reel-hero-bg video {
	position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.services-reel-hero-bg--placeholder .video-placeholder-inner {
	position: absolute; inset: 0; margin: 0; display: flex; flex-direction: column;
	align-items: center; justify-content: center; gap: 0.6rem;
	color: rgba(255,255,255,0.7);
}
/* 11 August, fourth pass — flipped and lightened per Soana: the previous
   version faded UP into a near-solid navy floor at the bottom (62%-100%
   above), which by design covered a big share of every clip — she felt
   it "disrupted" the footage rather than just grounding the text. The
   seam she actually wanted smoothed is the one at the very TOP, where
   the site's own navy .site-header bar meets this section — so the fade
   now runs the other way: solid navy at 0% (a seamless continuation of
   the header bar, no hard line), clearing to almost nothing by ~28%, and
   staying light the rest of the way down so the footage reads clearly
   through the middle and bottom where most of every clip's actual
   content sits. Nowhere near solid again at any point below the top —
   legibility for the now much-less-shielded eyebrow/title is handled by
   text-shadow below instead of a background floor. */
.services-reel-hero-scrim {
	position: absolute; inset: 0; z-index: 1; pointer-events: none;
	background: linear-gradient(180deg,
		var(--wt-navy) 0%,
		rgba(12,19,48,0.55) 10%,
		rgba(12,19,48,0.14) 28%,
		rgba(12,19,48,0.08) 60%,
		rgba(12,19,48,0.16) 100%);
}
.services-reel-hero-inner { position: relative; z-index: 2; text-align: center; width: 100%; }
/* Legibility safety net now that the scrim no longer gives the text a
   guaranteed opaque floor to sit on (see the fade comment above) — a
   drop shadow keeps the white eyebrow/title readable against whatever
   the footage looks like at that moment, bright daylight clips included,
   without needing to darken the video itself. */
.services-reel-hero .section-eyebrow, .services-reel-hero .services-title {
	text-shadow: 0 2px 14px rgba(6,11,28,0.7), 0 1px 3px rgba(6,11,28,0.8);
}
/* 11 August, third pass — restyled to match .hero-eyebrow/.hero-title on
   the homepage hero carousel (front-page.php) rather than the plainer
   whitish-grey treatment every other inner-page banner's eyebrow gets
   (.page-title-banner .section-eyebrow above) — Soana wants this page's
   header to read like the homepage hero, not a standard inner-page
   banner. Cyan accent + letter-spacing/size lifted straight from
   .hero-eyebrow; title given its own clamp() sized well below the
   homepage hero's own giant 3.6-6.4rem (this banner is deliberately
   smaller — see .services-reel-hero's min-height comment above) but with
   the same 700 weight it already inherits from the shared h1 rule. */
.services-reel-hero .section-eyebrow {
	justify-content: center; color: var(--wt-cyan);
	font-size: 0.95rem; letter-spacing: 0.04em;
}
/* Three-class selector (matches .page-title-banner .page-title's own
   pattern elsewhere in this file) needed to outrank body.wp-theme-
   wantok-new .services-title's navy color — a plain single-ancestor
   selector loses that specificity fight and the title rendered near-
   invisible dark navy text against this hero's own navy fade.
   11 August, sixth pass — bumped from clamp(2.1rem, 4.4vw, 3rem): Soana
   still wanted the card/vignette backdrop dropped (back to plain
   text-shadow above) but the text itself sized up a bit more to stand
   out against the footage. */
.page-content-template .services-reel-hero .services-title {
	margin: 0; color: #fff; font-size: clamp(2.4rem, 5vw, 3.4rem);
}
.services-reel-hero .page-title-banner-wave { position: absolute; left: 0; bottom: -1px; width: 100%; height: 34px; z-index: 3; }
@media (max-width: 700px) {
	.services-reel-hero { min-height: min(48vh, 360px); padding-bottom: 30px; }
}

/* About Us fan hero — replaces the old plain static banner photo for this
   page only. A curved, fanned row of real team headshots sits below the
   page eyebrow/title/quote/intro, which are shown visibly here (they were
   screen-reader-only before, hidden behind the plain photo). Ported from
   wantok-new-v59.zip's About Us redesign (17 July). */
.about-fan-hero {
	background: linear-gradient(180deg, var(--wt-surface) 0%, #ffffff 100%);
	padding: 56px 0 64px; text-align: center; overflow: hidden;
}
.about-fan-hero-inner { max-width: 720px; margin: 0 auto; padding: 0 22px; }
.about-fan-hero-eyebrow { display: inline-flex; }
.about-fan-hero-title {
	font-family: var(--wt-font-display); font-size: clamp(2rem, 4vw, 2.7rem);
	font-weight: 700; color: var(--wt-ink); margin: 0.3rem 0 0.6rem; line-height: 1.15;
}
.about-fan-hero-quote {
	font-family: var(--wt-font-display); font-style: italic; color: var(--wt-blue-deep);
	font-size: 1.1rem; margin: 0 0 0.4rem;
}
.about-fan-hero-intro { color: var(--wt-ink-2); font-size: 1.05rem; margin: 0; }

/* Fan hero is an auto-scrolling carousel of the full team roster (each
   photo appears twice back-to-back in the markup so the loop is seamless),
   arranged in a smooth repeating wave: larger portrait cards, tighter
   rounding, a pronounced arc, and edge cards hard-cropped by the container
   (no fade). Pauses on hover so a visitor can look at a photo. */
.about-fan-carousel {
	overflow: hidden; margin-top: 3rem; padding: 34px 0 46px;
}
.about-fan-track {
	display: flex; align-items: center; gap: 16px; width: max-content;
	animation: about-fan-scroll 50s linear infinite;
}
.about-fan-carousel:hover .about-fan-track { animation-play-state: paused; }
.about-fan-photo {
	position: relative; margin: 0; flex-shrink: 0; width: 170px; height: 220px;
	border-radius: 10px; overflow: hidden; box-shadow: var(--wt-shadow);
	transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.about-fan-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.about-fan-photo:hover { transform: translateY(-10px) scale(1.04) !important; box-shadow: var(--wt-shadow-lg); z-index: 5; }
/* Name/role label — hidden until the card is hovered. The carousel wave
   rotates each card individually via the nth-child rules below, but
   :hover already overrides that with a level translateY+scale transform
   (see above), so this label needs no counter-rotation of its own: by the
   time it's visible the card itself is already sitting level. */
.about-fan-photo-label {
	position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
	margin: 0; padding: 10px 12px 12px;
	background: linear-gradient(0deg, rgba(12,19,48,0.85) 0%, rgba(12,19,48,0.55) 65%, transparent 100%);
	color: #fff; opacity: 0; transform: translateY(6px);
	transition: opacity 0.2s ease, transform 0.2s ease;
	pointer-events: none;
}
.about-fan-photo:hover .about-fan-photo-label { opacity: 1; transform: translateY(0); }
/* 25 Aug 2026 — tap equivalent of the :hover rule above, for touch screens
   where hover doesn't really exist. theme.js's [data-tap-reveal] handler
   toggles .is-active on one photo at a time (tapping elsewhere, or the
   same photo again, clears it) — mirrors the hover transform too so a
   tapped photo lifts the same way a hovered one does. See the hint text
   this pairs with (.about-fan-hero-hint below). */
.about-fan-photo.is-active { transform: translateY(-10px) scale(1.04) !important; box-shadow: var(--wt-shadow-lg); z-index: 5; }
.about-fan-photo.is-active .about-fan-photo-label { opacity: 1; transform: translateY(0); }
.about-fan-photo-label-name {
	display: block; font-family: var(--wt-font-display); font-weight: 700;
	font-size: 0.82rem; line-height: 1.2;
}
.about-fan-photo-label-role { display: block; font-size: 0.68rem; color: var(--wt-on-dark-2); margin-top: 1px; }
@keyframes about-fan-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* Hint line under the carousel — plain-language nudge since the label
   reveal (hover on desktop, tap on touch — see above) has no other visual
   affordance hinting it exists. */
.about-fan-hero-hint {
	max-width: 520px; margin: 0.4rem auto 0; padding: 0 22px;
	font-size: 0.85rem; color: var(--wt-ink-3); text-align: center;
}
/* Smooth repeating wave — a full sine period across the 15-member roster
   so the curve loops with no visible seam between cycles. */
.about-fan-track .about-fan-photo:nth-child(15n+1)  { transform: rotate(0deg)    translateY(0px); }
.about-fan-track .about-fan-photo:nth-child(15n+2)  { transform: rotate(8deg)    translateY(2px); }
.about-fan-track .about-fan-photo:nth-child(15n+3)  { transform: rotate(15deg)   translateY(7px); }
.about-fan-track .about-fan-photo:nth-child(15n+4)  { transform: rotate(19deg)   translateY(14px); }
.about-fan-track .about-fan-photo:nth-child(15n+5)  { transform: rotate(20deg)   translateY(22px); }
.about-fan-track .about-fan-photo:nth-child(15n+6)  { transform: rotate(17deg)   translateY(30px); }
.about-fan-track .about-fan-photo:nth-child(15n+7)  { transform: rotate(12deg)   translateY(36px); }
.about-fan-track .about-fan-photo:nth-child(15n+8)  { transform: rotate(4deg)    translateY(40px); }
.about-fan-track .about-fan-photo:nth-child(15n+9)  { transform: rotate(-4deg)   translateY(40px); }
.about-fan-track .about-fan-photo:nth-child(15n+10) { transform: rotate(-12deg)  translateY(36px); }
.about-fan-track .about-fan-photo:nth-child(15n+11) { transform: rotate(-17deg)  translateY(30px); }
.about-fan-track .about-fan-photo:nth-child(15n+12) { transform: rotate(-20deg)  translateY(22px); }
.about-fan-track .about-fan-photo:nth-child(15n+13) { transform: rotate(-19deg)  translateY(14px); }
.about-fan-track .about-fan-photo:nth-child(15n+14) { transform: rotate(-15deg)  translateY(7px); }
.about-fan-track .about-fan-photo:nth-child(15n+15) { transform: rotate(-8deg)   translateY(2px); }
/* About Us fan carousel — responsive: just shrink the cards on smaller
   screens. No need to hide specific photos any more since this scrolls
   continuously rather than showing one fixed static set. */
@media (max-width: 860px) {
	.about-fan-photo { width: 130px; height: 168px; }
}
@media (max-width: 620px) {
	.about-fan-hero { padding: 40px 0 44px; }
	.about-fan-photo { width: 104px; height: 134px; }
}

.richtext-section { padding-top: 30px; padding-bottom: 30px; max-width: 900px; }

/* Full-bleed tinted band for a richtext section that needs more visual
   weight/separation mid-page (e.g. a section moved down below other
   content) — breaks out to the viewport edge with a soft surface tint and
   generous vertical breathing room, then centers its container as usual. */
.richtext-band { background: var(--wt-surface); padding: 64px 0; }
.richtext-section--band { padding-top: 0; padding-bottom: 0; }
.richtext-section.has-media { max-width: 1180px; display: grid; grid-template-columns: 1.1fr 1fr; gap: 2.5rem; align-items: center; }
.richtext-section h2 { position: relative; padding-bottom: 0.7rem; font-size: 1.5rem; }
.richtext-section h2::after { content: ""; position: absolute; left: 0; bottom: 0; width: 56px; height: 3px; border-radius: 2px; background: var(--wt-purple); }
.richtext-section p { color: var(--wt-ink-2); font-size: 1.03rem; }
/* Optional 'centered' flag (no side image) — center the heading/paragraphs
   and re-center the heading's underline accent to match. Ported from
   wantok-new-v59.zip's About Us redesign (17 July). */
.richtext-section--centered { text-align: center; }
.richtext-section--centered h2::after { left: 50%; transform: translateX(-50%); }

/* Optional 'signal' flag — swaps the plain underline bar for a small icon
   mark, used on About Us' "About WanTok Tonga" heading only. 25 Aug 2026:
   was a bespoke "signal-arc" SVG (concentric arcs); now reuses the
   homepage's own .why-spotlight/.why-icon glow-halo treatment instead
   (see wantok_block_richtext() in page-content-render.php) — same pulse
   animation and radial glow as "Why WanTok?"'s icons (.why-spotlight
   itself, incl. the halo-pulse keyframes, is defined further up this
   file), just re-sized/re-positioned here to sit where the old mark did. */
.richtext-section--signal h2::after { display: none; }
.richtext-signal-icon { width: 56px; height: 56px; margin: 0.1rem auto 1.1rem; }
.richtext-section--signal:not(.richtext-section--centered) .richtext-signal-icon { margin-left: 0; }
.richtext-copy > ul { list-style: none; display: grid; gap: 0.6rem; margin: 1rem 0; }
.richtext-copy > ul li { position: relative; padding-left: 1.9rem; color: var(--wt-ink-2); }
.richtext-copy > ul li::before {
	content: "\2713"; position: absolute; left: 0; top: 0.15em; width: 20px; height: 20px;
	border-radius: 999px; background: var(--wt-blue); color: #fff; font-size: 0.65rem; font-weight: 700;
	display: flex; align-items: center; justify-content: center; line-height: 1;
}

/* Optional 'compact' flag — same layout (image + copy side by side) as a
   normal richtext-section.has-media block, just trimmed down: a smaller
   photo and slightly smaller type. Added 27 August for Anker Solix's 6
   product sections — Soana preferred the original stacked layout over a
   card-grid redesign, just asked for it "a tad smaller". Scoped to this
   flag only (see wantok_block_richtext() in page-content-render.php), so
   every other richtext block site-wide is unaffected.

   Round 2, same day: with the padding trimmed down, the rows ran straight
   into each other with "no visual barrier" and felt monotone against the
   plain white page. Each row now sits in its own bordered, shadowed card
   (alternating white / tinted fill for a bit of rhythm) with real padding
   restored — still shorter overall than the original full-width sections,
   just contained instead of blurring together. Type spacing loosened
   slightly at the same time (line-height, list gap) since the card
   boundary now does the job of separating entries, so the text itself
   doesn't need to sit as tight. */
.richtext-section--compact {
	background: #fff; border: 1px solid var(--wt-border); border-radius: 16px;
	box-shadow: var(--wt-shadow); padding: 24px 26px; margin: 0 auto 20px;
}
.richtext-section--compact-alt { background: var(--wt-surface); }
.richtext-section--compact.has-media { max-width: 940px; gap: 1.8rem; grid-template-columns: 1.3fr 0.8fr; align-items: start; }
.richtext-section--compact .richtext-media { max-width: 280px; margin-left: auto; box-shadow: none; border: 1px solid var(--wt-border); }
.richtext-section--compact h2 { font-size: 1.15rem; padding-bottom: 0.4rem; margin-bottom: 0.6rem; }
.richtext-section--compact h2::after { width: 40px; height: 2px; }
.richtext-section--compact p { font-size: 0.93rem; line-height: 1.6; margin: 0 0 0.75rem; }
.richtext-section--compact .richtext-copy > ul { gap: 0.55rem; margin: 0.7rem 0 0; }
.richtext-section--compact .richtext-copy > ul li { font-size: 0.89rem; line-height: 1.5; padding-left: 1.6rem; }
.richtext-section--compact .richtext-copy > ul li::before { width: 16px; height: 16px; font-size: 0.55rem; }
@media (max-width: 860px) {
	.richtext-section--compact .richtext-media { max-width: 260px; margin: 0 auto; }
}

/* Glow accent (27 Aug, round 6) — Soana noticed the Anker product cards
   picked up the site's card-with-border look but not the coloured-ring-
   glow accent every other card grid site-wide carries (.plan-card.glow-
   cyan/-purple, .services-showcase-card.glow-cyan/-purple, .feature-
   mosaic-tile.glow-cyan/-purple — see those rules for the same language).
   Same tinted-border + soft-halo treatment, intensifying on hover; values
   match the other glow rules exactly so it reads as the same accent, not
   a new one. Alternated cyan/purple explicitly per product in
   page-content-data.php (the 'glow' flag on wantok_block_richtext()),
   same convention as wantok_get_services_showcase(). */
.richtext-section--compact.glow-cyan {
	border-color: color-mix(in srgb, var(--wt-cyan) 45%, transparent);
	box-shadow: 0 18px 40px rgba(12,19,48,0.1), 0 0 26px color-mix(in srgb, var(--wt-cyan) 20%, transparent);
}
.richtext-section--compact.glow-purple {
	border-color: color-mix(in srgb, var(--wt-purple) 45%, transparent);
	box-shadow: 0 18px 40px rgba(12,19,48,0.1), 0 0 26px color-mix(in srgb, var(--wt-purple) 20%, transparent);
}
.richtext-section--compact.glow-cyan:hover {
	border-color: color-mix(in srgb, var(--wt-cyan) 70%, transparent);
	box-shadow: var(--wt-shadow-lg), 0 0 38px color-mix(in srgb, var(--wt-cyan) 42%, transparent);
}
.richtext-section--compact.glow-purple:hover {
	border-color: color-mix(in srgb, var(--wt-purple) 70%, transparent);
	box-shadow: var(--wt-shadow-lg), 0 0 38px color-mix(in srgb, var(--wt-purple) 42%, transparent);
}

/* Anker Solix: comparison table + whole-home battery layout (27 August,
   round 2) — Soana felt these two sat "weirdly" once the products above
   became contained cards: the table was a bare bordered grid with a
   left-aligned heading, floating straight on the page with no container of
   its own, and the X1 section (fixed via 'band' => true on its own data
   entry above) suddenly looked oversized next to the now-compact product
   cards. The table gets the same card treatment as the products (border/
   shadow/radius) with a centred heading, so it reads as a deliberate
   "at a glance" summary rather than a leftover default table. Scoped to
   this page via :has() on the table's own section rather than editing
   wantok_block_table() itself, since that block type is shared with
   another page (broadband-plans) this shouldn't touch. */
.page-anker-solix .richtext-section:has(.data-table-wrap) {
	max-width: 940px; background: #fff; border: 1px solid var(--wt-border);
	border-radius: 16px; box-shadow: var(--wt-shadow); padding: 32px 28px;
	text-align: center;
	/* Extra breathing room above/below this card — Soana felt it sat right
	   on top of the last product card and the X1 band beneath it with
	   almost no gap between the three. */
	margin-top: 48px; margin-bottom: 48px;
}
.page-anker-solix .richtext-section:has(.data-table-wrap) h2 { display: inline-block; }
.page-anker-solix .richtext-section:has(.data-table-wrap) h2::after { left: 50%; transform: translateX(-50%); }
.page-anker-solix .data-table-wrap { border: none; margin-top: 1rem; text-align: left; }
.page-anker-solix .data-table thead th { background: var(--wt-surface-2); color: var(--wt-navy); }
.page-anker-solix .data-table tbody tr:nth-child(even) td { background: var(--wt-surface); }
/* The table itself is tucked behind a <details> toggle (see 'expandable'
   in wantok_block_table()) — the shared .table-details styling (its own
   border/background/padding box) would double up inside the card this
   section already has, so it's stripped back to a plain inline pill-style
   toggle here instead. */
.page-anker-solix .table-details { border: none; background: transparent; padding: 0; margin-top: 1.2rem; }
.page-anker-solix .table-details summary {
	display: inline-block; padding: 10px 34px 10px 20px; border: 1px solid var(--wt-border);
	border-radius: 999px; font-size: 0.9rem;
}
.page-anker-solix .table-details summary::after { right: 14px; }
.page-anker-solix .table-details[open] summary { margin-bottom: 0.6rem; }

.pill-row { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.pill { background: var(--wt-surface); border: 1px solid var(--wt-border); border-radius: 999px; padding: 0.5em 1.1em; font-size: 0.92rem; font-weight: 600; font-family: var(--wt-font-display); color: var(--wt-blue-deep); }

.feature-columns-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-top: 1.2rem; }
.feature-columns-grid .fcol { background: var(--wt-surface); border: 1px solid var(--wt-border); border-radius: var(--wt-radius); padding: 20px; }
.feature-columns-grid .fcol h3 { font-size: 1rem; margin-bottom: 0.6rem; }
.feature-columns-grid .fcol ul { list-style: none; display: grid; gap: 0.4rem; }
.feature-columns-grid .fcol li { font-size: 0.92rem; color: var(--wt-ink-2); position: relative; padding-left: 1.1rem; }
.feature-columns-grid .fcol li::before { content: ""; position: absolute; left: 0; top: 0.55em; width: 6px; height: 6px; border-radius: 999px; background: var(--wt-blue); }

.inner-plans-grid { max-width: none; }

.data-table-wrap { overflow-x: auto; margin-top: 1.2rem; border: 1px solid var(--wt-border); border-radius: var(--wt-radius-sm); }
.data-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.data-table th, .data-table td { padding: 10px 16px; border-bottom: 1px solid var(--wt-border); text-align: left; white-space: nowrap; }
.data-table thead th { background: var(--wt-surface); font-family: var(--wt-font-display); font-weight: 600; }
.data-table tbody tr:last-child td { border-bottom: none; }

.faq-list { display: grid; gap: 0.7rem; margin-top: 1.2rem; }
.faq-item { border: 1px solid var(--wt-border); border-radius: var(--wt-radius-sm); overflow: hidden; background: var(--wt-bg); }
.faq-item summary { cursor: pointer; padding: 14px 18px; font-weight: 600; font-family: var(--wt-font-display); list-style: none; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; float: right; color: var(--wt-blue); font-weight: 700; }
.faq-item[open] summary::after { content: "\2013"; }
.faq-item p { padding: 0 18px 16px; margin: 0; color: var(--wt-ink-2); }

/* "Upside-down triangle" team layout: rows of 5, 4, 3, 2 members (top to
   bottom), each row narrower than the last and its cards flexing to fill
   that row's width — so cards get wider row by row, tapering to a point
   at the single centered/featured card below. Row width is driven by
   data-count (set in the PHP loop) rather than a fixed column count. */
.team-pyramid { display: flex; flex-direction: column; align-items: center; gap: 1.4rem; margin-top: 1.4rem; }
.team-row { display: flex; justify-content: center; gap: 1.3rem; width: 100%; }
.team-row .team-card { flex: 1 1 0; min-width: 0; }
.team-row[data-count="5"] { max-width: 100%; }
.team-row[data-count="4"] { max-width: 84%; }
.team-row[data-count="3"] { max-width: 64%; }
.team-row[data-count="2"] { max-width: 44%; }
.team-row[data-count="1"] { max-width: 26%; }
.team-card {
	background: var(--wt-bg); border: 1px solid var(--wt-border); border-radius: var(--wt-radius);
	padding: 28px 20px 24px; text-align: center; box-shadow: var(--wt-shadow);
	transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.team-card:hover { transform: translateY(-4px); box-shadow: var(--wt-shadow-lg); border-color: transparent; }
.team-photo {
	width: 100px; height: 100px; margin: 0 auto 16px; border-radius: 50%;
	padding: 3px; background: var(--wt-grad);
}
.team-photo img {
	width: 100%; height: 100%; object-fit: cover; object-position: top center;
	border-radius: 50%; border: 3px solid var(--wt-bg); display: block;
}
.team-name { font-weight: 700; font-family: var(--wt-font-display); font-size: 1.04rem; margin: 0; }
.team-role {
	display: inline-block; margin: 0.55rem 0 0; padding: 3px 12px;
	background: var(--wt-grad-soft); color: var(--wt-blue-deep); border-radius: 999px;
	font-family: var(--wt-font-display); font-size: 0.7rem; font-weight: 600;
	letter-spacing: 0.03em; text-transform: uppercase; line-height: 1.4;
}

/* Featured team member(s) — e.g. the CEO — rendered in their own row below
   the main grid, centered, so they stand out at the bottom-middle instead
   of occupying a regular grid slot. A small eyebrow label and a slightly
   larger, gradient-bordered card mark it as the leadership spotlight while
   keeping the same card language as the rest of the grid. */
.team-featured-label {
	text-align: center; font-family: var(--wt-font-display); font-weight: 600;
	letter-spacing: 0.06em; text-transform: uppercase; font-size: 0.78rem;
	color: var(--wt-purple); margin: 2.2rem 0 0.9rem;
}
.team-grid-featured { display: flex; justify-content: center; }
.team-grid-featured .team-card {
	width: 100%; max-width: 280px; padding: 32px 24px 26px;
	border: 2px solid transparent;
	background: linear-gradient(var(--wt-bg),var(--wt-bg)) padding-box, var(--wt-grad) border-box;
	box-shadow: var(--wt-shadow-lg);
}
.team-grid-featured .team-photo { width: 116px; height: 116px; }

/* Simple photo grid — e.g. "the team in action" installs/field work —
   reusing the site's existing card shadow/hover-lift language but without
   any of the name/role chrome the team cards above have. Ported from
   wantok-new-v42.zip's About Us update (16 July). */
.gallery-intro { color: var(--wt-ink-2); margin-top: -0.4rem; }
.team-gallery {
	display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 1fr; gap: 1.1rem; margin-top: 1.4rem;
}
.team-gallery-item {
	margin: 0; border-radius: var(--wt-radius); overflow: hidden;
	box-shadow: var(--wt-shadow); aspect-ratio: 4 / 3; min-height: 0;
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.team-gallery-item:hover { transform: translateY(-4px); box-shadow: var(--wt-shadow-lg); }
.team-gallery-item img {
	width: 100%; height: 100%; object-fit: cover; object-position: center; display: block;
}

/* Photo feature grid — About Us "Our Team in Action", spotlighting real
   field work. Ported from wantok-new-v59.zip's About Us redesign (17
   July), enlarged + given the homepage's coloured-ring-glow treatment on
   25 Aug 2026 (.glow-cyan/.glow-purple, alternated per tile in
   wantok_block_feature_cards() — see that function's comment) — same
   border-tint + soft box-shadow-glow pattern as .plans .plan-card.glow-
   cyan/.glow-purple above, just not re-scoped to a `.plans` ancestor
   since this grid is the only place these tile classes are used. */
.feature-cards-section { max-width: 1180px; }
/* 25 Aug 2026, round 2 — after a flat bento (3 wide tiles + 6 narrow, all
   with a caption card underneath) Soana asked to see a few different
   arrangements and picked this one: a "hero mosaic" — one photo promoted
   to a true 2x2 anchor tile, the rest as smaller image-forward tiles with
   the caption overlaid on the photo instead of sitting in a card below
   it. 1 hero (4 grid units) + 8 small (1 unit each) = 12 units, an exact
   multiple of the 4-column grid, so `dense` auto-flow always fills clean
   3-row rectangle with no stray gaps (checked via the harness/Playwright,
   same way the previous bento's row math was verified). Row height and
   type size here are deliberately a step up from the first mosaic sketch
   Soana was shown — that one ran small-tile captions at 0.8rem in ~130px
   cells; bumped both so nothing on the live page reads as cramped. */
.feature-mosaic-grid {
	display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: 175px;
	grid-auto-flow: row dense; gap: 1.4rem; margin-top: 1.4rem;
}
.feature-mosaic-tile {
	position: relative; grid-column: span 1; grid-row: span 1;
	border: 1px solid var(--wt-border); border-radius: var(--wt-radius); overflow: hidden;
	box-shadow: var(--wt-shadow); transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}
.feature-mosaic-tile.is-hero { grid-column: span 2; grid-row: span 2; }
/* Same coloured-ring-glow language as .plans .plan-card.glow-cyan/-purple
   and the previous flat bento's .feature-card-tile.glow-* — a tinted
   border plus a soft blurred halo, not the thin hard-edged outline this
   mosaic shipped with first (0 0 0 2px, no blur — read as a rigid ring
   rather than a glow, since these tiles are edge-to-edge photos with no
   card padding around them to soften it). */
.feature-mosaic-tile.glow-cyan {
	border-color: color-mix(in srgb, var(--wt-cyan) 55%, transparent);
	box-shadow: var(--wt-shadow), 0 0 22px color-mix(in srgb, var(--wt-cyan) 45%, transparent);
}
.feature-mosaic-tile.glow-purple {
	border-color: color-mix(in srgb, var(--wt-purple) 55%, transparent);
	box-shadow: var(--wt-shadow), 0 0 22px color-mix(in srgb, var(--wt-purple) 45%, transparent);
}
.feature-mosaic-tile:hover { transform: translateY(-4px); }
.feature-mosaic-tile.glow-cyan:hover {
	border-color: color-mix(in srgb, var(--wt-cyan) 75%, transparent);
	box-shadow: var(--wt-shadow-lg), 0 0 34px color-mix(in srgb, var(--wt-cyan) 60%, transparent);
}
.feature-mosaic-tile.glow-purple:hover {
	border-color: color-mix(in srgb, var(--wt-purple) 75%, transparent);
	box-shadow: var(--wt-shadow-lg), 0 0 34px color-mix(in srgb, var(--wt-purple) 60%, transparent);
}
.feature-mosaic-tile-img {
	width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.35s ease;
}
.feature-mosaic-tile:hover .feature-mosaic-tile-img { transform: scale(1.06); }
/* Optional locality tag — grounds the photo in *where* the work happened
   (reusing wording already present in the item's own copy, e.g.
   "Tongatapu" / "Outer Islands" — not a new claim, just surfaced as a
   small on-photo label). */
.feature-mosaic-tile-tag {
	position: absolute; top: 12px; left: 12px; z-index: 2;
	background: rgba(255,255,255,0.92); color: var(--wt-navy);
	font-family: var(--wt-font-display); font-weight: 700;
	font-size: 0.66rem; letter-spacing: 0.03em; text-transform: uppercase;
	padding: 4px 10px; border-radius: 999px;
}
/* The darkening backdrop is a separate layer sized to a fixed share of the
   tile (not to the caption's own height) so a short one-line heading gets
   just as much contrast as a two-line one — on the first pass the gradient
   was sized to the text box itself, which left almost no dark backdrop
   behind a short caption and made it nearly vanish over a bright photo
   (e.g. "Marine & Remote Access" over open water — caught via the harness
   render, not visible in the flat mockup preview). */
.feature-mosaic-tile::after {
	content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 62%; z-index: 1;
	background: linear-gradient(to top, rgba(12,19,48,0.88), rgba(12,19,48,0) 100%);
	pointer-events: none;
}
.feature-mosaic-tile.is-hero::after { height: 52%; background: linear-gradient(to top, rgba(12,19,48,0.9), rgba(12,19,48,0) 100%); }
.feature-mosaic-tile-overlay {
	position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 12px 14px;
}
/* Boosted specificity (body.wp-theme-wantok-new prefix) — same fix as
   .services-title etc. above: the sitewide body.wp-theme-wantok-new
   h1-h4 rule outranks a plain single-class selector and was silently
   forcing these headings back to dark ink, which is invisible over a
   photo. Caught via the harness render (a small tile over a dark photo —
   "Starlink Onboard" — went completely unreadable), not visible in the
   flat HTML mockup preview since that page never loads style.css's
   sitewide h1-h4 rule the same way the real theme does. */
body.wp-theme-wantok-new .feature-mosaic-tile-overlay h3 {
	color: #fff; margin: 0; font-size: 0.95rem; font-weight: 600; line-height: 1.3;
	text-shadow: 0 1px 4px rgba(0,0,0,0.35);
}
.feature-mosaic-tile.is-hero .feature-mosaic-tile-overlay { padding: 24px 26px; }
.feature-mosaic-tile.is-hero .feature-mosaic-tile-overlay h3 { font-size: 1.5rem; }
.feature-mosaic-tile.is-hero .feature-mosaic-tile-overlay p {
	color: rgba(255,255,255,0.88); margin: 8px 0 0; font-size: 0.95rem; line-height: 1.5; max-width: 36ch;
	text-shadow: 0 1px 4px rgba(0,0,0,0.35);
}
@media (max-width: 860px) {
	/* 2-column tablet grid — the hero still spans both columns across two
	   rows (4 units), the 8 small tiles fill the remaining 8 units below
	   it two-per-row, so the grid still closes out clean with no gaps. */
	.feature-mosaic-grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 155px; }
}
@media (max-width: 620px) {
	.feature-mosaic-grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 130px; gap: 1rem; }
	.feature-mosaic-tile.is-hero .feature-mosaic-tile-overlay { padding: 16px 16px; }
	.feature-mosaic-tile.is-hero .feature-mosaic-tile-overlay h3 { font-size: 1.15rem; }
	.feature-mosaic-tile.is-hero .feature-mosaic-tile-overlay p { font-size: 0.85rem; }
}

.content-todo-note {
	background: var(--wt-grad-soft); border: 1px dashed var(--wt-purple); border-radius: var(--wt-radius-sm);
	padding: 14px 18px; font-size: 0.94rem; color: var(--wt-ink-2);
}
.content-todo-note strong { color: var(--wt-purple-deep); }

/* Widgets */
.widget { margin-bottom: 1.6rem; }
.footer-widget .widget-title { color: #fff; }

/* ---------- Reveal animation ----------
   Content is fully visible by default. Hiding only happens when JS has
   added .wt-js to <html> AND motion is allowed, so no-JS / reduced-motion
   users always see content.
   27 Aug (round 2, "richer, still tasteful") — the base entrance picked up
   a gentle scale + blur pop on top of the original rise, and borrowed the
   smoother expo-out easing that used to be exclusive to .reveal-elegant
   below, so the whole site's baseline reveal now reads as more considered
   without tipping into a bounce or a flashy slide. Kept subtle on purpose:
   4% scale and a light 4px blur are felt more than seen. */
.wt-js .reveal-init {
	opacity: 0;
	transform: translateY(26px) scale(0.96);
	filter: blur(4px);
	transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1), transform 0.8s cubic-bezier(0.16, 1, 0.3, 1), filter 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}
.wt-js .reveal-init.is-visible { opacity: 1; transform: none; filter: blur(0); }

/* Directional/elegance modifiers for specific sections that want a more
   deliberate slide-in than the default rise (e.g. About Us' "About WanTok
   Tonga" copy/photo pair). Layered on top of .reveal-init so no-JS /
   reduced-motion visitors still just see the content — see above. The
   expo-out easing curve reads as a slower, smoother "settle into place"
   rather than a snappy bounce, which is what gives it the elegant/
   professional feel instead of a flashy one. Ported from
   wantok-new-v59.zip's About Us redesign (17 July). */
.wt-js .reveal-init.reveal-elegant { transition-duration: 0.9s; transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
.wt-js .reveal-init.reveal-left  { transform: translateX(-36px); }
.wt-js .reveal-init.reveal-right { transform: translateX(36px); }
.wt-js .reveal-init.reveal-up    { transform: translateY(30px); }
.wt-js .reveal-init.reveal-left.is-visible,
.wt-js .reveal-init.reveal-right.is-visible,
.wt-js .reveal-init.reveal-up.is-visible { opacity: 1; transform: none; }

/* Card-grid stagger (27 Aug; delays lengthened slightly in round 2) — same
   .reveal-init fade/rise, just offset per card so a row that enters the
   viewport together cascades in left to right instead of popping as one
   flat block. Capped at the 6th child; IntersectionObserver fires each
   card independently as it scrolls into view, so cards beyond a first
   on-screen row don't need (or get) the extra wait. */
.wt-js .services-showcase-grid > .services-showcase-card:nth-child(1),
.wt-js .plans-grid > .plan-card:nth-child(1),
.wt-js .news-grid > .news-card:nth-child(1),
.wt-js .trust-inline-list > .trust-inline-item:nth-child(1),
.wt-js .why-list > li:nth-child(1) { transition-delay: 0s; }
.wt-js .services-showcase-grid > .services-showcase-card:nth-child(2),
.wt-js .plans-grid > .plan-card:nth-child(2),
.wt-js .news-grid > .news-card:nth-child(2),
.wt-js .trust-inline-list > .trust-inline-item:nth-child(2),
.wt-js .why-list > li:nth-child(2) { transition-delay: 0.1s; }
.wt-js .services-showcase-grid > .services-showcase-card:nth-child(3),
.wt-js .plans-grid > .plan-card:nth-child(3),
.wt-js .news-grid > .news-card:nth-child(3),
.wt-js .trust-inline-list > .trust-inline-item:nth-child(3),
.wt-js .why-list > li:nth-child(3) { transition-delay: 0.2s; }
.wt-js .services-showcase-grid > .services-showcase-card:nth-child(4),
.wt-js .trust-inline-list > .trust-inline-item:nth-child(4) { transition-delay: 0.3s; }
.wt-js .services-showcase-grid > .services-showcase-card:nth-child(5) { transition-delay: 0.4s; }
.wt-js .services-showcase-grid > .services-showcase-card:nth-child(6) { transition-delay: 0.5s; }
/* 27 Aug (round 2) — the showcase grid actually runs 8 cards (2 full rows
   at the 4-column desktop width), and both rows are close enough together
   that they're often in view at once; the delay table used to stop at the
   6th child, so cards 7-8 fell back to 0 delay and visibly popped in
   before cards 5-6 finished waiting — a cascade that looked out of order
   rather than left-to-right, row by row. Continuing the same +0.1s step
   fixes it. */
.wt-js .services-showcase-grid > .services-showcase-card:nth-child(7) { transition-delay: 0.6s; }
.wt-js .services-showcase-grid > .services-showcase-card:nth-child(8) { transition-delay: 0.7s; }

/* Icon pop-in (27 Aug, round 2) — the icon/illustration inside each "What
   we do" card, each Why WanTok list item, and each trust-strip item scales
   in with a light overshoot just after its card/item has started rising
   into place, so it reads as its own small "arrival" rather than moving as
   a flat part of the card. Scoped under .wt-js like everything else here,
   so no-JS/reduced-motion visitors just see icons in place with no hidden
   state to ever get stuck in. cubic-bezier(0.34,1.56,0.64,1) is a standard
   "back" ease — it overshoots past 100% then settles, which is what gives
   the small bounce without it feeling like a toy.
   .services-showcase-photo covers the real current content (every "What we
   do" card today uses a character-illustration photo, not the plain icon
   fallback) — it already carries `transform: translateX(-50%)` for its own
   centering, so the hidden/visible states here compose scale onto that
   same translateX rather than replacing it, or the photo would jump
   sideways. .services-showcase-icon-badge is kept alongside it for the
   icon-only fallback path in wantok_render_services_showcase_grid(), which
   has no pre-existing transform to preserve. */
.wt-js .services-showcase-icon-badge,
.wt-js .why-icon,
.wt-js .trust-inline-icon {
	/* No display override here — .why-icon relies on display:flex plus
	   margin:0 auto (from its own base rule) to stay centered as a block
	   box; changing its outer display type here would break that
	   centering. transform/opacity/transition layer on top fine either
	   way since they don't touch the box's display type. */
	transform: scale(0.4);
	opacity: 0;
	transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.15s, opacity 0.4s ease 0.15s;
}
.wt-js .services-showcase-photo {
	transform: translateX(-50%) scale(0.4);
	opacity: 0;
	transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.15s, opacity 0.4s ease 0.15s;
}
.wt-js .services-showcase-card.is-visible .services-showcase-icon-badge,
.wt-js .why-list > li.is-visible .why-icon,
.wt-js .trust-inline-item.is-visible .trust-inline-icon { transform: scale(1); opacity: 1; }
.wt-js .services-showcase-card.is-visible .services-showcase-photo { transform: translateX(-50%) scale(1); opacity: 1; }

/* Divider grow-in (27 Aug, round 2) — the thin vertical rule that already
   sits between each section's heading and its intro copy (services/plans/
   why/news) now grows in from a zero-height line instead of just fading
   in flat with the rest of the header block. Reuses the existing
   .section-head[data-reveal] as the trigger (.is-visible lands on the
   heading wrapper itself), so this needs no new JS hooks. transform-origin
   defaults to center, so it grows outward from the middle rather than
   downward from the top, matching a rule that visually sits between two
   lines of text rather than hanging off one edge. */
.wt-js .services-divider,
.wt-js .plans-divider,
.wt-js .why-divider,
.wt-js .news-divider {
	transform: scaleY(0);
	transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.2s;
}
.wt-js .services-head.is-visible .services-divider,
.wt-js .plans-head.is-visible .plans-divider,
.wt-js .why-head.is-visible .why-divider,
.wt-js .news-head.is-visible .news-divider { transform: scaleY(1); }

@media (prefers-reduced-motion: reduce) {
	.wt-js .reveal-init { opacity: 1; transform: none; filter: none; transition: none; }
	.wt-js .services-showcase-icon-badge,
	.wt-js .why-icon,
	.wt-js .trust-inline-icon { transform: none; opacity: 1; transition: none; }
	/* translateX(-50%) kept (not "none") — it's load-bearing centering on
	   .services-showcase-photo, not part of the pop-in effect itself. */
	.wt-js .services-showcase-photo { transform: translateX(-50%); opacity: 1; transition: none; }
	.wt-js .services-divider,
	.wt-js .plans-divider,
	.wt-js .why-divider,
	.wt-js .news-divider { transform: none; transition: none; }
}

/* =====================================================================
   Responsive
   ===================================================================== */
@media (max-width: 980px) {
	/* Homepage revamp (embed pass) — dots are bottom-center at every width
	   now (see the base .hero-carousel-dots rule above), so the override
	   that used to reposition them below 980px is gone; .hero-copy-stack/
	   .hero-actions no longer carry a left offset to reset either. */
	.news-grid { grid-template-columns: repeat(2, 1fr); }
	/* The left-body / divider / right-heading row needs real width to
	   read as a row — stack it below tablet width, with the divider
	   flipping from a vertical rule to a horizontal one between them. */
	.services-head { flex-direction: column; align-items: flex-start; gap: 1.4rem; }
	.services-divider { width: 100%; height: 1px; align-self: auto; }
	.services-grid { grid-template-columns: repeat(2, 1fr); }
	/* Gap shrinks with the grid (was 84px/32px on desktop) but stays wide
	   enough to clear each photo's ~46px overlap over the row above —
	   .services-showcase-grid only, used on the dedicated Services and
	   Plans page, not the homepage grid above. */
	.services-showcase-grid { grid-template-columns: repeat(2, 1fr); gap: 64px 28px; }
	.plans-head { flex-direction: column; align-items: flex-start; gap: 1.4rem; }
	.plans-divider { width: 100%; height: 1px; align-self: auto; }
	.plans-grid { grid-template-columns: repeat(2, 1fr); }
	.news-head { flex-direction: column; align-items: flex-start; gap: 1.4rem; }
	.news-divider { width: 100%; height: 1px; align-self: auto; }
	.plan-card:nth-child(3) { grid-column: 1 / -1; }
	.why-head { flex-direction: column; align-items: flex-start; gap: 1.4rem; }
	.why-divider { width: 100%; height: 1px; align-self: auto; }
	/* The grow-in above animates these as vertical rules (scaleY); once
	   they flip to horizontal here, growing on Y is invisible on a 1px-tall
	   bar, so the reveal switches to scaleX (grows left-to-right from
	   center) instead — same trigger, same timing, just the axis that
	   matches the bar's actual orientation at this width. */
	.wt-js .services-divider,
	.wt-js .plans-divider,
	.wt-js .why-divider,
	.wt-js .news-divider { transform: scaleX(0); }
	.wt-js .services-head.is-visible .services-divider,
	.wt-js .plans-head.is-visible .plans-divider,
	.wt-js .why-head.is-visible .why-divider,
	.wt-js .news-head.is-visible .news-divider { transform: scaleX(1); }
	/* Every *-heading/*-body pair below uses flex: 1 1 200px / 380px so the
	   two columns start at a sensible width in the desktop row layout
	   above. Once .services-head/.plans-head/.why-head/.news-head switch
	   to flex-direction: column right above, that same flex-basis applies
	   to the vertical axis instead — silently forcing each block to a
	   200-380px-tall box regardless of how short its actual text is. That
	   was the real source of the big gaps between the section heading and
	   its intro paragraph on mobile (15 July feedback). Resetting the
	   basis to auto here lets each block size to its real content. */
	.services-heading, .services-body,
	.plans-heading, .plans-body,
	.why-heading, .why-body,
	.news-heading, .news-head-intro {
		flex-basis: auto;
	}
	.why-list { grid-template-columns: repeat(2, 1fr); }
	.trust-inline-list { grid-template-columns: repeat(2, 1fr); row-gap: 1.6rem; }
	.trust-inline-item:nth-child(2n+1) { border-left: none; }
	.trust-inline-item:nth-child(n+3) { border-top: 1px solid rgba(255,255,255,0.18); padding-top: 1.4rem; }
	.feature-columns-grid { grid-template-columns: repeat(2, 1fr); }
	/* The triangle taper only reads well on wide screens — below this, every
	   row wraps to a plain 2-up grid so cards stay a comfortable, legible size. */
	.team-row { flex-wrap: wrap; max-width: 100% !important; }
	.team-row .team-card { flex: 1 1 45%; }
	.team-gallery { grid-template-columns: repeat(2, 1fr); }
	.richtext-section.has-media { grid-template-columns: 1fr; }
	.richtext-section.has-media .richtext-media { order: -1; }
	.hero { min-height: min(80vh, 620px); }
	.page-title-banner { padding: 38px 0 30px; }
}

@media (max-width: 620px) {
	.feature-columns-grid { grid-template-columns: 1fr; }
	.team-row .team-card { flex: 1 1 100%; }
	.team-gallery { grid-template-columns: 1fr; }
}

@media (max-width: 860px) {
	.menu-toggle { display: inline-flex; }
	.main-navigation { margin-left: auto; }
	/* 30 Aug 2026 — this stays the same proven full off-canvas flyout
	   mechanism as before (JS in theme.js is completely unchanged —
	   .is-open toggling, Escape-to-close, .submenu-toggle accordions, all
	   identical), reskinned navy to match the desktop "Network Rail" look.
	   The vertical line-and-node "rail" from the desktop nav carries down
	   into this list instead of being dropped at the breakpoint (see
	   .nav-menu::before/a::after below). inset's top value is the real
	   measured height of utility-bar + nav-row together at mobile width
	   (see wantok_header_network_rail_revert_30aug.md for how this was
	   re-measured after the floating-card padding was removed, and after
	   My WanTok moving back into the utility bar pushed the phone/email
	   wrap point up from 459px to ~572px) — covers 573-860px; below that,
	   the phone number + email in the utility bar wrap onto two lines
	   (not enough width for both side by side, and Soana's hard
	   requirement is both always visible, so letting it wrap rather than
	   truncating either one), which grows the header, so that narrower
	   band gets its own taller override just below. */
	.nav-menu {
		position: fixed; inset: 114px 0 0 0; flex-direction: column; align-items: stretch;
		gap: 2px; background: var(--wt-navy); padding: 18px 22px 96px; overflow-y: auto;
		transform: translateX(100%); transition: transform 0.28s ease; z-index: 90;
		border-top: 1px solid rgba(255,255,255,0.12);
	}
	.main-navigation.is-open .nav-menu { transform: translateX(0); }
	.nav-menu a {
		padding: 0.9rem 0.6rem 0.9rem 1.6rem; font-size: 1.06rem; white-space: normal;
		border-bottom: 1px solid rgba(255,255,255,0.1); color: var(--wt-on-dark); background: none;
	}
	.nav-menu a:hover, .nav-menu .current-menu-item > a {
		color: #fff; background: rgba(255,255,255,0.08); box-shadow: none;
	}
	/* Vertical rail: same line-and-node signature as the desktop nav (see
	   the base .nav-menu::before/a::after rules above), just turned 90°
	   to run down the list instead of along a single row. */
	.nav-menu::before { left: 6px; right: auto; top: 8px; bottom: 8px; width: 1px; height: auto; }
	.nav-menu a::after { left: 6px; bottom: auto; top: 50%; transform: translate(-50%, -50%); }
	.nav-menu li { position: static; }
	.nav-menu .sub-menu {
		position: static; opacity: 1; visibility: visible; transform: none; box-shadow: none;
		border: none; background: none; padding: 0 0 0 14px; display: none;
	}
	.nav-menu li.submenu-open > .sub-menu { display: block; }
	.nav-menu .mega-menu {
		position: static; opacity: 1; visibility: visible; transform: none; box-shadow: none;
		border: none; background: none; width: auto; padding: 0 0 0 26px; display: none;
		grid-template-columns: 1fr; gap: 18px;
	}
	.nav-menu li.submenu-open > .mega-menu { display: grid; }
	/* Reset the desktop rounded-glow-tile treatment back to a plain
	   stacked section — it's flush against the same navy flyout background
	   as everything else here, not a floating card that needs its own
	   fill/border/shadow — and recolour all four groups' text for
	   readability against navy (desktop's ink/purple/grey palette is
	   tuned for the white dropdown surface, not this panel). */
	.mega-col { padding: 0; border-radius: 0; background: none; border: none; box-shadow: none; }
	.mega-col-title, .mega-col--cyan .mega-col-title, .mega-col--purple .mega-col-title { color: var(--wt-cyan); }
	.mega-col li a { color: var(--wt-on-dark); }
	.mega-col li a:hover { color: #fff; background: rgba(255,255,255,0.08); }
	.mega-item-disabled { color: var(--wt-on-dark-2); }
	.mega-col--panel { background: none; }
	.mega-col--panel .mega-col-title { color: var(--wt-cyan); }
	.mega-col--panel .mega-col-desc { color: var(--wt-on-dark-2); }
	.mega-col--panel .mega-item-disabled { color: var(--wt-on-dark-2); }
	.mega-col--panel .mega-badge { background: var(--wt-cyan); color: var(--wt-navy); }
	.submenu-toggle {
		display: inline-grid; place-items: center; position: absolute; right: 8px;
		width: 34px; height: 34px; margin-top: 6px; background: rgba(255,255,255,0.1);
		border: 1px solid rgba(255,255,255,0.25); border-radius: 8px; cursor: pointer;
		font-size: 1.2rem; color: #fff;
	}
	.nav-menu .menu-item-has-children { position: relative; }
	/* My WanTok lives in the utility bar again (see header.php/.utility-
	   actions above) — it's visible in the collapsed mobile bar the same
	   way the phone/email pairing is, no separate mobile-only pinning
	   needed any more (the "Network Dock" round's .nav-card-portal
	   show/hide-on-open rule this comment used to describe is gone along
	   with the card it lived in). */
}

/* Narrow-phone exception referenced above — the utility bar's phone
   number + email don't both fit on one line under ~572px wide (with My
   WanTok back in that bar taking its own share of the row), so they wrap
   to a second line and the whole header (and therefore the flyout's
   starting point) is taller here than the 573-860px range. */
@media (max-width: 572px) {
	.nav-menu { inset: 143px 0 0 0; }
}

@media (max-width: 620px) {
	body { font-size: 16px; }
	.news-grid { grid-template-columns: 1fr; }
	.trust-inline-list { grid-template-columns: 1fr; gap: 1.4rem; }
	.trust-inline-item { align-items: center; padding: 0; border-left: none !important; border-top: none !important; }
	.why-list { grid-template-columns: 1fr; }
	.services-grid { grid-template-columns: 1fr; }
	/* .services-showcase-grid only — dedicated Services and Plans page. */
	.services-showcase-grid { grid-template-columns: 1fr; gap: 56px; max-width: 380px; margin-left: auto; margin-right: auto; }
	.plans-grid { grid-template-columns: 1fr; }
	.plan-card:nth-child(3) { grid-column: auto; }
	.utility-bar { font-size: 0.8rem; }
	.utility-contact { gap: 0.8rem; }
	.hero { min-height: min(76vh, 560px); }
	.hero-inner { padding: 64px 22px; }
	/* The calc()-based min-height on the base .hero-copy-stack rule
	   undershoots badly at mobile widths (reported 15 July: title running
	   into the buttons on phones). Two compounding reasons: (1) below this
	   breakpoint .hero-title drops its 9.15em cap for max-width:100%, so
	   each of the 5 slides wraps to a different, width-dependent line
	   count instead of a consistent 3 — measured up to 4 lines / ~444px
	   tall on some slide+width combinations; (2) .hero-copy is
	   position:absolute, so when a slide's real content is taller than
	   the reserved box, it doesn't push .hero-actions down — it just
	   overlaps it directly, which is exactly the bug reported. Fixed with
	   a flat override sized to the worst case measured across every
	   width from 320-620px (444px content height, on the Solar slide at
	   exactly 320px — title reworded 27 Aug, see front-page.php, but the
	   measurement itself is unaffected since the new title is a similar
	   length), plus a buffer. A few slides get a little extra breathing
	   room above the
	   buttons at some widths as a result — better than any slide ever
	   overlapping them. */
	.hero-copy-stack { min-height: 470px; }
	.hero-title { max-width: 100%; }
	.hero-carousel-dots { bottom: 44px; }
	.post-card { grid-template-columns: 1fr; }
	.post-card-thumb img { height: 200px; }
	.section-head-row { flex-direction: column; align-items: flex-start; }

	/* Section vertical padding was still the desktop value (80-108px top
	   AND bottom) at every width — the grids inside collapse to a single
	   column below, but nothing reduced the space bracketing each
	   section, so on a phone it read as far too much dead air between
	   sections (15 July feedback: "many spaces inbetween the sections"). */
	.trust-strip { padding: 32px 0 36px; }
	.services { padding: 56px 0 64px; }
	.why { padding: 56px 0 64px; }
	.plans { padding: 56px 0; }
	.latest-news { padding: 56px 0; }
	.cta-inner { padding: 56px 20px; }
}

/* =====================================================================
   Broadband Plans v2 — "How It Works" step timeline + alternating
   light/navy section bands + white-glass pricing cards (cyan/purple SVG
   dial rings) + two-column FAQ. Merged 19 August 2026 from the approved
   HTML mockup (broadband-plans-v3-howitworks.html) into the real theme.
   Powers the new 'pricing-cards-v2' / 'how-it-works' / 'faq-v2' block
   types in page-content-render.php — see wantok_block_pricing_cards_v2(),
   wantok_block_how_it_works() and wantok_block_faq_v2() there. Added as
   new, separately-named classes (not edits to the existing .plan-card/
   .faq-item/.fcol system) so every other page still using the original
   'cards'/'faq'/'columns' block types is completely unaffected.
   ===================================================================== */

  /* ---- alternating section bands ---- */
  .band-light { background: var(--wt-bg); padding: 64px 0; }
  .band-tint { background: #f4f6fb; padding: 64px 0; }
  .band-navy { background: var(--wt-navy); padding: 64px 0; }

  /* ---- FAQ v2: two-column, left heading / right hairline-divided rows ----
     Matches the reference screenshot's layout, recoloured for our white
     section instead of its original dark background. */
  .faq-wrap { max-width: var(--wt-maxw); margin: 0 auto; padding: 0 24px; display: grid; grid-template-columns: minmax(200px, 300px) 1fr; gap: 48px; align-items: start; }
  .faq-heading { font-size: 2.1rem; line-height: 1.18; margin: 0; text-align: left; }
  .faq-list-v2 { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--wt-border); }
  .faq-row { border-bottom: 1px solid var(--wt-border); }
  .faq-row summary {
    cursor: pointer; padding: 18px 4px; display: flex; justify-content: space-between; align-items: flex-start;
    gap: 20px; list-style: none; font-weight: 600; font-family: var(--wt-font-display); color: var(--wt-ink);
  }
  .faq-row summary::-webkit-details-marker { display: none; }
  .faq-row summary::after { content: "+"; color: var(--wt-blue); font-weight: 700; flex-shrink: 0; }
  .faq-row[open] summary::after { content: "\2013"; }
  .faq-row p { margin: 0; padding: 0 4px 18px; color: var(--wt-ink-2); max-width: 640px; }

  @media (max-width: 700px) {
    .faq-wrap { grid-template-columns: 1fr; gap: 20px; }
  }

  .band-head { max-width: var(--wt-maxw); margin: 0 auto 40px; padding: 0 24px; text-align: center; }
  .band-eyebrow { font-family: var(--wt-font-display); font-weight: 700; font-size: 0.76rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--wt-blue); margin: 0 0 8px; }
  .band-navy .band-eyebrow { color: var(--wt-cyan); }
  .band-title { font-size: 1.9rem; margin: 0 0 10px; }
  .band-navy .band-title { color: #fff; }
  .band-lead { color: var(--wt-ink-2); max-width: 640px; margin: 0 auto; line-height: 1.55; }
  .band-navy .band-lead { color: rgba(255,255,255,0.72); }

  /* ---- How It Works: icon row with a connecting line ----
     27 August — switched from a tall vertical timeline (each step full-
     width, stacked, connected by a vertical rail) to a horizontal 3-across
     row on wider screens — Soana flagged the vertical version as "too
     large" (it ran ~1500px tall on desktop for just 3 steps). Below the
     781px breakpoint this reverts to the original vertical timeline
     (unchanged from before this pass) since 3 columns get cramped once a
     step's copy has to wrap onto many lines in a narrow column. Shared by
     every "...-plans" page that uses the 'how-it-works' block type
     (Broadband, Starlink, Business Solutions, Cybersecurity, AI
     Implementation), so this is a one-place fix for all of them. */
  .hiw-steps { max-width: 760px; margin: 0 auto; padding: 0 24px; display: grid; gap: 0; position: relative; }
  .hiw-step { display: grid; grid-template-columns: 64px 1fr; gap: 22px; padding: 0 0 40px; position: relative; }
  .hiw-step:last-child { padding-bottom: 0; }
  .hiw-rail { position: relative; display: flex; justify-content: center; }
  .hiw-rail::before {
    content: ''; position: absolute; top: 64px; bottom: -40px; left: 50%; width: 2px;
    background: var(--wt-border); transform: translateX(-50%);
  }
  .hiw-step:last-child .hiw-rail::before { display: none; }
  .hiw-icon {
    width: 64px; height: 64px; border-radius: 14px; background: var(--wt-bg); border: 1px solid var(--wt-border);
    display: flex; align-items: center; justify-content: center; position: relative; z-index: 1;
    box-shadow: var(--wt-shadow-lg);
  }
  .hiw-icon svg { width: 28px; height: 28px; stroke: var(--wt-blue-deep); fill: none; stroke-width: 1.6; }
  .hiw-dot { position: absolute; left: 50%; top: 64px; width: 8px; height: 8px; border-radius: 50%; background: var(--wt-blue); transform: translate(-50%, 26px); z-index: 1; }
  .hiw-step:last-child .hiw-dot { display: none; }
  .hiw-body { padding-top: 8px; }
  .hiw-num { font-family: var(--wt-font-display); font-weight: 700; font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--wt-blue); margin: 0 0 4px; }
  .hiw-step-title { font-size: 1.1rem; margin: 0 0 6px; }
  .hiw-step-copy { color: var(--wt-ink-2); line-height: 1.55; margin: 0; max-width: 480px; }

  /* How It Works now sits on navy — re-colour the text so nothing washes out.
     Two-class selectors needed for the headings: a bare .hiw-step-title rule
     loses to the sitewide body.wp-theme-wantok-new h1-h4 rule on specificity
     (same recurring trap as .pc-name/.fcol h3 elsewhere on this page). */
  .band-navy .hiw-num { color: var(--wt-cyan); }
  .band-navy .hiw-step-title { color: #fff; }
  .band-navy .hiw-step-copy { color: rgba(255,255,255,0.72); }
  .band-navy .hiw-rail::before { background: rgba(255,255,255,0.2); }
  .band-navy .hiw-dot { background: var(--wt-cyan); }

  /* ---- Horizontal layout, 781px and up ----
     Same markup, same components (.hiw-icon/.hiw-num/.hiw-step-title/
     .hiw-step-copy untouched) — just re-flows the 3 steps into a row
     instead of a stack, and swaps the vertical connecting rail for a
     single horizontal line running behind all 3 icons. */
  @media (min-width: 781px) {
    .hiw-steps {
      max-width: var(--wt-maxw); grid-template-columns: repeat(3, 1fr); column-gap: 32px; padding-top: 6px;
    }
    .hiw-steps::before {
      content: ''; position: absolute; top: 38px; left: calc(100% / 6); right: calc(100% / 6);
      height: 2px; background: var(--wt-border); z-index: 0;
    }
    .band-navy .hiw-steps::before { background: rgba(255,255,255,0.2); }
    .hiw-step {
      grid-template-columns: none; display: flex; flex-direction: column; align-items: center;
      text-align: center; padding: 0 12px; gap: 0;
    }
    .hiw-rail { margin: 0 0 20px; z-index: 1; }
    /* The old vertical connector segment and mid-line dot are replaced by
       the single .hiw-steps::before line above — hide both rather than
       removing their rules, so the vertical layout below 781px (which
       still uses them) is untouched. */
    .hiw-rail::before, .hiw-dot { display: none; }
    .hiw-body { padding-top: 0; }
    .hiw-step-copy { max-width: 260px; margin: 0 auto; }

    /* 30 August 2026 — UMS Plans' "How It Works" is the first page to use 4
       steps instead of the usual 3 (Assess/Install/Monitor/Act); the base
       rules above are hardcoded to 3 columns, so this modifier (added by
       wantok_block_how_it_works() only when there are exactly 4 steps)
       widens the grid and re-centres the connecting line to match. Every
       other page's 3-step layout is untouched. */
    .hiw-steps.hiw-steps-4 { grid-template-columns: repeat(4, 1fr); }
    .hiw-steps.hiw-steps-4::before { left: 12.5%; right: 12.5%; }
  }

  @media (max-width: 640px) {
    .hiw-step { grid-template-columns: 52px 1fr; gap: 16px; }
    .hiw-icon { width: 52px; height: 52px; }
    .hiw-icon svg { width: 22px; height: 22px; }
    .hiw-rail::before { top: 52px; }
    .hiw-dot { top: 52px; }
  }

  /* ---- Pricing v5: WHITE glass cards, single row, cyan (Home) / purple (Business) ----
     Soana asked for cyan+purple specifically (reference screenshot), which reverses
     the earlier "no purple on this page" call from an earlier round — a deliberate,
     explicit request, scoped to this card treatment only. Round 6: dropped the
     decorative ring backdrop, and flipped the cards themselves from dark glass to
     white glass now that the section behind them is white. */
  .pc-section { position: relative; overflow: hidden; }
  .pc-tags { position: relative; z-index: 1; display: flex; justify-content: space-between; align-items: center; max-width: var(--wt-maxw); margin: 0 auto 18px; padding: 0 24px; }
  .pc-tag { font-family: var(--wt-font-display); font-weight: 700; font-size: 0.8rem; letter-spacing: 0.14em; text-transform: uppercase; }
  .pc-tag.is-home { color: var(--wt-cyan); }
  .pc-tag.is-mid { color: var(--wt-blue-deep); }
  .pc-tag.is-business { color: var(--wt-purple); }

  .pc-grid { position: relative; z-index: 1; max-width: var(--wt-maxw); margin: 0 auto; padding: 0 24px; display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; }
  .pc-card {
    /* overflow:hidden added 20 August alongside .pc-badge-strip below —
       lets that strip's square top corners get clipped to the card's own
       rounded corners for free, same trick .plan-flag already relied on
       elsewhere in this file. */
    position: relative; overflow: hidden;
    background: linear-gradient(160deg, rgba(255,255,255,0.92), rgba(255,255,255,0.72) 60%);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    border-radius: 16px; padding: 28px 18px 24px; text-align: center;
    display: flex; flex-direction: column; border: 1px solid rgba(12,19,48,0.08);
    box-shadow: 0 14px 32px rgba(12,19,48,0.07);
    transition: transform 0.2s ease;
  }
  .pc-card.is-home { border-color: rgba(19,182,230,0.5); box-shadow: 0 14px 32px rgba(12,19,48,0.05), 0 0 0 1px rgba(19,182,230,0.16) inset, 0 18px 40px rgba(19,182,230,0.16); }
  /* 30 August 2026 — 'is-mid', a blue middle tier added for UMS Plans'
     3-tier Essentials(cyan)/Sub-Metering(blue)/Guardian(purple) lineup — see
     wantok_ums_plans_rebuild_30aug.md. Existing 2-tier pages never use this
     class. */
  .pc-card.is-mid { border-color: rgba(17,112,214,0.45); box-shadow: 0 14px 32px rgba(12,19,48,0.05), 0 0 0 1px rgba(17,112,214,0.16) inset, 0 18px 40px rgba(17,112,214,0.16); }
  .pc-card.is-business { border-color: rgba(122,57,187,0.5); box-shadow: 0 14px 32px rgba(12,19,48,0.05), 0 0 0 1px rgba(122,57,187,0.16) inset, 0 18px 40px rgba(122,57,187,0.16); }
  .pc-card:hover { transform: translateY(-3px); }

  /* Signal-rail graphic — an alternative to the speed dial above for plans
     measured by "how many points are monitored" rather than a headline
     speed (first use: UMS Plans). Each .pc-rail is one measured point; the
     dot sweeps to show it's a live reading, not a static badge. */
  .pc-signal { display: grid; gap: 6px; margin: 0 0 10px; }
  .pc-rail { position: relative; height: 3px; border-radius: 999px; background: rgba(12,19,48,0.08); }
  .pc-rail-dot { position: absolute; top: 50%; width: 6px; height: 6px; margin-top: -3px; border-radius: 50%; background: var(--wt-cyan); animation: pc-sweep 3.4s ease-in-out infinite; }
  .pc-card.is-mid .pc-rail-dot { background: var(--wt-blue-deep); }
  .pc-card.is-business .pc-rail-dot { background: var(--wt-purple-deep); }
  .pc-rail:nth-child(2) .pc-rail-dot { animation-delay: -0.9s; }
  .pc-rail:nth-child(3) .pc-rail-dot { animation-delay: -1.9s; }
  @keyframes pc-sweep { 0%, 100% { left: 3%; } 50% { left: 88%; } }
  .pc-signal-label { font-size: 0.68rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--wt-ink-3); font-weight: 600; margin: 0 0 14px; }
  @media (prefers-reduced-motion: reduce) { .pc-rail-dot { animation: none; left: 3%; } }
  /* Guardian's 24/7 alert-monitoring cue — a small pulsing dot in the
     card's top-right corner, the one visual signal for its always-on
     anomaly alerts (the feature that sets it apart from the tier below). */
  .pc-alert { position: absolute; top: 16px; right: 16px; width: 9px; height: 9px; border-radius: 50%; background: var(--wt-purple-deep); z-index: 1; }
  .pc-alert::before {
    content: ''; position: absolute; inset: -7px; border-radius: 50%;
    border: 1.5px solid var(--wt-purple); opacity: 0.7;
    animation: pc-pulse-ring 2.4s ease-out infinite;
  }
  @keyframes pc-pulse-ring { 0% { transform: scale(0.5); opacity: 0.75; } 100% { transform: scale(2.1); opacity: 0; } }
  @media (prefers-reduced-motion: reduce) { .pc-alert::before { animation: none; opacity: 0.4; } }

  /* Progress-ring dial (round 7): thick track + rounded-cap coloured arc,
     like the reference screenshot — track recoloured light (was dark navy in
     the reference) since it now sits on our white card, so it stays subtle
     instead of reading as a heavy dark circle. */
  .pc-dial-wrap { position: relative; width: 72px; height: 72px; margin: 0 auto 16px; }
  .pc-dial-ring { width: 72px; height: 72px; transform: rotate(-90deg); display: block; }
  .pc-dial-track { fill: none; stroke: rgba(12,19,48,0.10); stroke-width: 7; }
  .pc-dial-arc { fill: none; stroke-width: 7; stroke-linecap: round; }
  .pc-dial-arc.is-home { stroke: var(--wt-cyan); }
  .pc-dial-arc.is-mid { stroke: var(--wt-blue-deep); }
  .pc-dial-arc.is-business { stroke: var(--wt-purple); }
  .pc-dial-inner {
    position: absolute; inset: 9px; border-radius: 50%; background: var(--wt-navy-2); display: flex; flex-direction: column;
    align-items: center; justify-content: center; line-height: 1.1;
  }
  .pc-dial-mbps { font-family: var(--wt-font-display); font-weight: 700; font-size: 0.9rem; color: #fff; }
  .pc-dial-unit { font-size: 0.54rem; color: rgba(255,255,255,0.55); text-transform: uppercase; letter-spacing: 0.04em; }
  .pc-card .pc-name { font-family: var(--wt-font-display); font-weight: 700; font-size: 1.05rem; margin: 0 0 6px; color: var(--wt-ink); }
  /* min-height was 2.8em (2 lines) but WanTok Connect/Bronze/Smart/Super's
     longer descriptions actually wrap to 3 lines at this card width, so
     their price line sat lower than WanTok Lite's (whose shorter text fits
     in 2 lines) — bumped to fit the tallest real case (3 lines) so every
     card's price sits on the same baseline regardless of description length. */
  .pc-for { font-size: 0.78rem; color: var(--wt-ink-2); line-height: 1.4; margin: 0 0 14px; min-height: 4.3em; }
  /* "Most Popular" tag — a full-width banner strip across the very TOP of
     the card, above the dial. Two earlier shapes (a rotated corner ribbon,
     then an inset corner pill) both risked colliding with the dial at this
     card's width; a strip in its own row can't overlap anything below it.
     Negative margins (matching .pc-card's own 28px/18px top/side padding)
     pull it flush to the card's edges so it spans edge-to-edge; .pc-card's
     overflow:hidden above then rounds its top corners to match the card.
     wantok_render_pc_card() ALWAYS prints this element (see that function)
     so the row reserves the same height whether or not that particular
     card is the featured one — the non-featured cards' strips are left
     empty, and :empty makes those invisible while still taking up the
     same space, keeping the dial/name/price rows aligned across every
     card regardless of which one (if any) carries the label. Fixed
     height (not padding sized by content) because an EMPTY strip has no
     line-box of its own and would otherwise collapse shorter than the
     filled one. */
  .pc-badge-strip {
    margin: -28px -18px 18px; height: 30px;
    display: flex; align-items: center; justify-content: center;
    background: var(--wt-navy); color: #fff; text-align: center;
    font-family: var(--wt-font-display); font-weight: 700; font-size: 0.72rem; letter-spacing: 0.02em;
  }
  .pc-badge-strip:empty { background: transparent; }
  .pc-price { font-family: var(--wt-font-display); font-weight: 700; font-size: 1.4rem; color: var(--wt-ink); margin: 0 0 2px; }
  .pc-unit { font-size: 0.74rem; color: var(--wt-ink-3); margin: 0 0 16px; }
  .pc-feats { list-style: none; margin: 0 0 18px; padding: 0; display: grid; gap: 6px; text-align: left; }
  .pc-feats li { font-size: 0.78rem; color: var(--wt-ink-2); padding-left: 16px; position: relative; }
  .pc-card.is-home .pc-feats li::before { content: ''; position: absolute; left: 0; top: 0.55em; width: 6px; height: 6px; border-radius: 50%; background: var(--wt-cyan); }
  .pc-card.is-mid .pc-feats li::before { content: ''; position: absolute; left: 0; top: 0.55em; width: 6px; height: 6px; border-radius: 50%; background: var(--wt-blue-deep); }
  .pc-card.is-business .pc-feats li::before { content: ''; position: absolute; left: 0; top: 0.55em; width: 6px; height: 6px; border-radius: 50%; background: var(--wt-purple); }
  /* A feature line carried over from the tier below ("Everything in X,
     plus:") — muted, with its bullet recoloured to match, so it reads as
     context rather than a fresh selling point. */
  .pc-feats li.is-carried { color: var(--wt-ink-3); font-weight: 600; }
  .pc-feats li.is-carried::before { background: var(--wt-ink-3) !important; }
  .pc-cta {
    /* a <button> (opens the enquiry modal below) rather than the earlier
       mailto: <a> — width/cursor/appearance reset so it still reads as
       the same pill-style link, not a default form button. */
    margin-top: auto; display: block; width: 100%; text-align: center; padding: 10px 14px; border-radius: var(--wt-radius-sm);
    font-family: var(--wt-font-display); font-weight: 600; font-size: 0.85rem;
    background: transparent; color: var(--wt-ink); border: 1px solid rgba(12,19,48,0.15);
    appearance: none; cursor: pointer;
  }
  .pc-card.is-home .pc-cta { color: var(--wt-blue-deep); border-color: rgba(19,182,230,0.5); }
  .pc-card.is-mid .pc-cta { color: var(--wt-blue-deep); border-color: rgba(17,112,214,0.5); }
  .pc-card.is-business .pc-cta { color: var(--wt-purple-deep); border-color: rgba(122,57,187,0.5); }
  .pc-card.is-home .pc-cta:hover { background: var(--wt-cyan); color: #fff; border-color: var(--wt-cyan); }
  .pc-card.is-mid .pc-cta:hover { background: var(--wt-blue-deep); color: #fff; border-color: var(--wt-blue-deep); }
  .pc-card.is-business .pc-cta:hover { background: var(--wt-purple); color: #fff; border-color: var(--wt-purple); }
  .pc-note { position: relative; z-index: 1; max-width: var(--wt-maxw); margin: 28px auto 0; padding: 0 24px; text-align: center; font-size: 0.82rem; color: var(--wt-ink-2); }

  /* ---- Feature accordion (30 August) — two fixed columns of expandable
     rows, reusing the same <details>/<summary> pattern as the real FAQ
     accordion. First use: UMS Plans' "What the UMS Does" (7 capabilities,
     too repetitive as a card grid) — see wantok_block_feature_accordion()
     and wantok_ums_plans_rebuild_30aug.md. Columns are fixed, not CSS
     `columns` auto-balance, so opening one row only grows its own column
     instead of reshuffling rows between columns. */
  .facc-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 0 56px; max-width: 900px; margin: 8px auto 0; }
  .facc-col { border-top: 1px solid var(--wt-border); }
  .facc-row { border-bottom: 1px solid var(--wt-border); }
  .facc-row summary {
    cursor: pointer; padding: 18px 2px; display: flex; align-items: center; gap: 14px;
    list-style: none; font-family: var(--wt-font-display); font-weight: 600; font-size: 0.98rem; color: var(--wt-ink);
  }
  .facc-row summary::-webkit-details-marker { display: none; }
  .facc-row-icon { width: 20px; height: 20px; flex-shrink: 0; }
  .facc-row-icon svg { width: 20px; height: 20px; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  .facc-col:first-child .facc-row:nth-child(odd) .facc-row-icon svg { stroke: var(--wt-blue-deep); }
  .facc-col:first-child .facc-row:nth-child(even) .facc-row-icon svg { stroke: var(--wt-purple-deep); }
  .facc-col:last-child .facc-row:nth-child(odd) .facc-row-icon svg { stroke: var(--wt-cyan); }
  .facc-col:last-child .facc-row:nth-child(even) .facc-row-icon svg { stroke: var(--wt-blue-deep); }
  .facc-row summary .facc-plus { margin-left: auto; color: var(--wt-blue); font-weight: 700; font-size: 1.1rem; flex-shrink: 0; }
  .facc-row summary .facc-plus::after { content: '+'; }
  .facc-row[open] summary .facc-plus::after { content: '\2013'; }
  .facc-row p { margin: 0; padding: 0 2px 20px 34px; color: var(--wt-ink-2); font-size: 0.88rem; line-height: 1.6; }
  .facc-badge { font-family: var(--wt-font-display); font-weight: 700; font-size: 0.6rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--wt-ink-3); background: var(--wt-surface-2); border: 1px solid var(--wt-border); border-radius: 999px; padding: 2px 8px; margin-left: 2px; }
  @media (max-width: 700px) {
    .facc-cols { grid-template-columns: 1fr; gap: 0; }
  }

  /* ---- Flat services grid (23 August) — icon + name + description only,
     no price/dial/CTA. Used by wantok_block_service_cards() for the Plans
     pages that quote individually rather than publish fixed prices
     (Business Solutions Plans, Cybersecurity Plans) — see the 'service-cards'
     case in page-content-render.php and the comment on those two entries in
     page-content-data.php. Reuses .pc-grid/.pc-card so these sit visually
     consistent with the pricing-card Plans pages (Broadband, Starlink). */
  .svc-grid { align-items: stretch; }
  .svc-card { justify-content: flex-start; }
  .svc-card-icon {
    width: 64px; height: 64px; border-radius: 50%; background: var(--wt-surface-2);
    display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; flex-shrink: 0;
  }
  .svc-card-icon svg { width: 28px; height: 28px; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
  .pc-card.is-home .svc-card-icon svg { stroke: var(--wt-blue-deep); }
  .pc-card.is-business .svc-card-icon svg { stroke: var(--wt-purple-deep); }
  .svc-card .pc-name { min-height: 2.6em; display: flex; align-items: center; justify-content: center; }
  .svc-card-desc { font-size: 0.82rem; line-height: 1.55; color: var(--wt-ink-2); margin: 0; }
  @media (min-width: 981px) {
    .svc-card-desc { min-height: 6.6em; }
  }

  @media (max-width: 980px) {
    .pc-grid { grid-template-columns: repeat(2, 1fr); }
  }
  @media (max-width: 480px) {
    .pc-grid { grid-template-columns: 1fr; }
  }

  /* ---------- Sliding-capsule category selector ("Option B") ----------
     Added 21 August for Starlink Plans (see wantok_render_pc_tabs() in
     page-content-render.php) — an iOS-style segmented control, one
     continuous track with a white capsule that slides to sit behind
     whichever category is active. Reuses the existing .plan-tab-input
     radios / input[id$="-N"]:checked suffix-matching convention already
     defined above (.plan-tab-panel etc.) — only the nav's look is new. */
  .starlink-tabs { position: relative; z-index: 1; max-width: var(--wt-maxw); margin: 0 auto; padding: 0 24px; }
  .starlink-tabs .pc-grid { padding: 0; margin: 0 auto; }

  .pc-seg-track {
    position: relative; display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px;
    width: 100%; max-width: 520px; margin: 0 auto 1.8rem; background: var(--wt-surface-2);
    border: 1px solid var(--wt-border); border-radius: 12px; padding: 5px; border: 0;
  }
  .pc-seg-indicator {
    position: absolute; top: 5px; left: 5px; bottom: 5px; width: calc(33.333% - 6px);
    background: #fff; border-radius: 9px; box-shadow: 0 3px 10px rgba(12,19,48,0.16), 0 0 0 1px rgba(12,19,48,0.03);
    transition: transform 0.28s cubic-bezier(.4,0,.2,1); z-index: 0;
  }
  .pc-seg-item {
    position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 5px;
    padding: 12px 6px; border-radius: 9px; cursor: pointer; user-select: none;
    font-family: var(--wt-font-display); font-weight: 600; font-size: 0.82rem; color: var(--wt-ink-3);
    transition: color 0.2s ease; text-align: center;
  }
  .pc-seg-icon { width: 18px; height: 18px; color: var(--wt-ink-3); transition: color 0.2s ease; }
  .pc-seg-icon svg { width: 100%; height: 100%; display: block; }
  input[id$="-0"]:checked ~ .pc-seg-track .pc-seg-indicator { transform: translateX(0%); }
  input[id$="-1"]:checked ~ .pc-seg-track .pc-seg-indicator { transform: translateX(100%); }
  input[id$="-2"]:checked ~ .pc-seg-track .pc-seg-indicator { transform: translateX(200%); }
  input[id$="-0"]:checked ~ .pc-seg-track label[for$="-0"],
  input[id$="-1"]:checked ~ .pc-seg-track label[for$="-1"],
  input[id$="-2"]:checked ~ .pc-seg-track label[for$="-2"] { color: var(--wt-navy); }
  input[id$="-0"]:checked ~ .pc-seg-track label[for$="-0"] .pc-seg-icon,
  input[id$="-1"]:checked ~ .pc-seg-track label[for$="-1"] .pc-seg-icon,
  input[id$="-2"]:checked ~ .pc-seg-track label[for$="-2"] .pc-seg-icon { color: var(--wt-blue); }
  input[id$="-0"]:focus-visible ~ .pc-seg-track label[for$="-0"],
  input[id$="-1"]:focus-visible ~ .pc-seg-track label[for$="-1"],
  input[id$="-2"]:focus-visible ~ .pc-seg-track label[for$="-2"] { outline: 3px solid var(--wt-blue); outline-offset: 2px; border-radius: 9px; }
  @media (max-width: 700px) {
    .pc-seg-item { font-size: 0.72rem; padding: 10px 4px; }
    .pc-seg-icon { width: 16px; height: 16px; }
  }

  /* ---------- Terminal kit — always-visible section ----------
     Sits right under the plan-category tabs, inside the same section, so
     the page reads as "1. pick a plan category above, 2. pick your kit
     here" rather than gating the kit behind a click (see wantok_render_
     pc_tabs()'s docblock for the full /frontend-design reasoning on why
     this isn't a 4th tab). Reuses .band-eyebrow/.band-lead for consistent
     type with the rest of the page; .pc-kits-title is a smaller heading
     than .band-title on purpose — this is a secondary heading within the
     Plans section, not a new band. */
  .pc-kits-divider { border-top: 1px solid var(--wt-border); max-width: var(--wt-maxw); margin: 3rem auto 2.5rem; }
  .pc-kits-head { max-width: 640px; margin: 0 auto 2rem; text-align: center; }
  .pc-kits-title { font-size: 1.4rem; margin: 0 0 10px; }

  /* Starlink's 6 Local Priority plans, one row — Soana asked for all cards
     in a category to sit in a single row rather than wrapping. Narrower
     card padding/type below keeps 6-across readable instead of just
     shrinking the shared .pc-card rules sitewide (only applied under this
     page-scoped class, not the base .pc-grid). */
  @media (min-width: 981px) {
    .pc-grid.cols-6-row { grid-template-columns: repeat(6, 1fr); max-width: none; gap: 10px; }
    .pc-grid.cols-6-row .pc-card { padding: 20px 10px 18px; }
    .pc-grid.cols-6-row .pc-dial-wrap { width: 56px; height: 56px; }
    .pc-grid.cols-6-row .pc-dial-ring { width: 56px; height: 56px; }
    .pc-grid.cols-6-row .pc-dial-mbps { font-size: 0.76rem; }
    .pc-grid.cols-6-row .pc-dial-unit { font-size: 0.46rem; }
    .pc-grid.cols-6-row .pc-name { font-size: 0.88rem; }
    .pc-grid.cols-6-row .pc-for { font-size: 0.68rem; min-height: 2.4em; }
    .pc-grid.cols-6-row .pc-price { font-size: 1.1rem; }
    .pc-grid.cols-6-row .pc-unit { font-size: 0.66rem; margin-bottom: 10px; }
    .pc-grid.cols-6-row .pc-feats li { font-size: 0.68rem; }
    .pc-grid.cols-6-row .pc-cta { font-size: 0.74rem; padding: 8px 6px; }
    .pc-grid.cols-4 { grid-template-columns: repeat(4, 1fr); max-width: var(--wt-maxw); }
    .pc-grid.cols-3 { grid-template-columns: repeat(3, 1fr); max-width: 900px; }
    .pc-grid.cols-2 { grid-template-columns: repeat(2, 1fr); max-width: 620px; }
  }

  /* Starlink's "Good to Know Before You Order" band (wantok_block_pc_
     install_note()) — a short bullet list of install caveats above the
     collapsed reference tables (which reuse .table-details/.data-table,
     already defined above). */
  .starlink-info-wrap { max-width: var(--wt-maxw); margin: 0 auto; padding: 0 24px; }
  .starlink-info-list { list-style: none; margin: 0 0 1.6rem; padding: 0; display: grid; gap: 0.7rem; max-width: 780px; }
  .starlink-info-list li { position: relative; padding-left: 1.6rem; color: var(--wt-ink-2); font-size: 0.98rem; }
  .starlink-info-list li::before {
    content: ''; position: absolute; left: 0; top: 0.5em; width: 8px; height: 8px; border-radius: 50%; background: var(--wt-blue);
  }

  /* "Select Plan" enquiry modal — see the wantok_block_pricing_cards_v2()
     PHP comment (page-content-render.php) for why this replaced the
     earlier mailto: link. Kept deliberately quiet: one accent colour
     (navy, echoing the "Most Popular" banner) rather than tinting it per
     tier, so the one thing that stands out on this page stays the
     pricing cards themselves. The small eyebrow above the title IS
     tier-tinted (cyan/purple) purely as a wayfinding echo of the card
     the person just clicked, not a second competing accent. */
  .plan-modal-overlay {
    position: fixed; inset: 0; z-index: 80; display: flex; align-items: center; justify-content: center;
    padding: 20px; background: rgba(12,19,48,0.55);
    opacity: 0; visibility: hidden; transition: opacity 0.18s ease;
  }
  .plan-modal-overlay.is-open { opacity: 1; visibility: visible; }
  .plan-modal {
    position: relative; width: 100%; max-width: 420px; max-height: calc(100vh - 40px); overflow-y: auto;
    background: var(--wt-bg); border-radius: 20px; box-shadow: var(--wt-shadow-lg);
    padding: 36px 32px 32px; transform: translateY(10px) scale(0.98); transition: transform 0.18s ease;
  }
  .plan-modal-overlay.is-open .plan-modal { transform: translateY(0) scale(1); }
  @media (prefers-reduced-motion: reduce) {
    .plan-modal-overlay, .plan-modal { transition: none; }
  }
  .plan-modal-close {
    position: absolute; top: 14px; right: 14px; width: 32px; height: 32px; display: grid; place-items: center;
    background: none; border: none; border-radius: 50%; font-size: 1.5rem; line-height: 1; color: var(--wt-ink-3);
    cursor: pointer;
  }
  .plan-modal-close:hover { background: var(--wt-surface); color: var(--wt-ink); }
  .plan-modal-eyebrow {
    font-family: var(--wt-font-display); font-weight: 700; font-size: 0.7rem; letter-spacing: 0.1em;
    text-transform: uppercase; margin: 0 0 8px; color: var(--wt-ink-3);
  }
  .plan-modal-overlay.is-home .plan-modal-eyebrow { color: var(--wt-cyan); }
  .plan-modal-overlay.is-mid .plan-modal-eyebrow { color: var(--wt-blue-deep); }
  .plan-modal-overlay.is-business .plan-modal-eyebrow { color: var(--wt-purple); }
  .plan-modal-title { font-family: var(--wt-font-display); font-weight: 700; font-size: 1.3rem; margin: 0 0 4px; color: var(--wt-ink); }
  .plan-modal-sub { font-size: 0.92rem; color: var(--wt-ink-2); margin: 0 0 24px; }
  .plan-field { display: block; margin-bottom: 14px; }
  .plan-field span { display: block; font-size: 0.8rem; font-weight: 600; color: var(--wt-ink-2); margin-bottom: 6px; }
  .plan-field input, .plan-field textarea {
    width: 100%; box-sizing: border-box; padding: 10px 13px; border-radius: 8px;
    border: 1px solid var(--wt-border); background: var(--wt-surface);
    font-family: var(--wt-font-body); font-size: 0.95rem; color: var(--wt-ink);
    transition: border-color 0.15s ease;
  }
  /* Textarea sibling (27 Aug, added for the Support page's inline "Send us
     a message" form — wantok_block_contact_form_split() — the plan-modal
     form this class set was built for only ever needed single-line inputs
     until now) */
  .plan-field textarea { min-height: 92px; resize: vertical; font-family: var(--wt-font-body); }
  .plan-field input:focus, .plan-field textarea:focus { border-color: var(--wt-cyan); }
  .plan-modal-submit {
    width: 100%; margin-top: 6px; padding: 12px 14px; border: none; border-radius: 8px;
    background: var(--wt-navy); color: #fff; font-family: var(--wt-font-display); font-weight: 700; font-size: 0.92rem;
    cursor: pointer; transition: background 0.15s ease;
  }
  .plan-modal-submit:hover { background: #171f45; }
  .plan-modal-submit:disabled { opacity: 0.6; cursor: default; }
  .plan-modal-note { font-size: 0.78rem; color: var(--wt-ink-3); margin: 12px 0 0; line-height: 1.5; }
  .plan-modal-error { font-size: 0.82rem; color: #b3261e; margin: 12px 0 0; }
  .plan-modal-success-title { font-family: var(--wt-font-display); font-weight: 700; font-size: 1.15rem; margin: 0 0 8px; color: var(--wt-ink); }
  .plan-modal-success-body { font-size: 0.95rem; color: var(--wt-ink-2); margin: 0 0 24px; line-height: 1.5; }

/* =========================================================================
   HOMEPAGE REVAMP — embed pass (24 Aug 2026)
   Everything below is new, added to bring the homepage in line with the
   Navy-Dominant direction Soana approved across the design-exploration
   rounds (see PROJECT-NOTES.md). Kept as one block, clearly marked, rather
   than interleaved with the original stylesheet, so a future pass can find
   or remove it in one place. New class names throughout (.inquiry-modal-*,
   .why-ambient-field, .why-spotlight, .service-char*) don't collide with
   anything already in this file or in page-content-render.php.
   ========================================================================= */

/* ---- Content-section type: Manrope (headings/body) + IBM Plex Mono
   (small caps/labels), homepage only. Header, hero and footer stay on the
   original Space Grotesk/Inter (this rule only touches the sections below
   the hero) — fonts themselves are enqueued site-wide in functions.php
   for simplicity, but only applied here. Each section redefines the same
   --wt-font-display/--wt-font-body custom properties already used
   throughout this file, so every existing rule that reads them (card
   titles, buttons, nav, etc.) picks up the new family automatically
   within these sections without needing its own override. */
:root { --wt-font-mono: 'IBM Plex Mono', ui-monospace, monospace; }
/* .cta-band deliberately excluded — that section class is shared with
   other pages' own closing CTA band (page-content-render.php), so it
   stays on the site's original type rather than risk bleeding Manrope
   onto pages this revamp isn't meant to touch. */
.trust-strip, .why, .services, .plans, .latest-news {
	--wt-font-display: 'Manrope', sans-serif;
	--wt-font-body: 'Manrope', sans-serif;
}
.trust-inline-detail, .latest-news .news-date { font-family: var(--wt-font-mono); }

/* ---- Inquiry modal — general "Send us an enquiry" form opened from the
   hero's "Contact Us" button and the Business Connect / Satellite &
   Remote plan cards' "Talk to us" buttons (see [data-inquiry-trigger] in
   front-page.php and the matching JS in theme.js). Adapted from this same
   file's .plan-modal-* above (the real, already-live "Select Plan" form)
   with the per-plan price/tier prefill dropped, since this version is
   general-purpose rather than tied to one plan. Deliberately its own
   class names/IDs throughout so it never collides with .plan-modal-*. */
.inquiry-modal-overlay {
	position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center;
	padding: 20px; background: rgba(12,19,48,0.55);
	opacity: 0; visibility: hidden; transition: opacity 0.18s ease;
}
.inquiry-modal-overlay.is-open { opacity: 1; visibility: visible; }
.inquiry-modal {
	position: relative; width: 100%; max-width: 420px; max-height: calc(100vh - 40px); overflow-y: auto;
	background: var(--wt-bg); border-radius: 20px; box-shadow: var(--wt-shadow-lg);
	padding: 36px 32px 32px; transform: translateY(10px) scale(0.98); transition: transform 0.18s ease;
}
.inquiry-modal-overlay.is-open .inquiry-modal { transform: translateY(0) scale(1); }
@media (prefers-reduced-motion: reduce) {
	.inquiry-modal-overlay, .inquiry-modal { transition: none; }
}
.inquiry-modal-close {
	position: absolute; top: 14px; right: 14px; width: 32px; height: 32px; display: grid; place-items: center;
	background: none; border: none; border-radius: 50%; font-size: 1.5rem; line-height: 1; color: var(--wt-ink-3);
	cursor: pointer;
}
.inquiry-modal-close:hover { background: var(--wt-surface); color: var(--wt-ink); }
.inquiry-modal-eyebrow {
	font-family: 'Space Grotesk', sans-serif; font-weight: 700; font-size: 0.7rem; letter-spacing: 0.1em;
	text-transform: uppercase; margin: 0 0 8px; color: var(--wt-cyan);
}
.inquiry-modal-title { font-family: 'Space Grotesk', sans-serif; font-weight: 700; font-size: 1.3rem; margin: 0 0 4px; color: var(--wt-ink); }
.inquiry-modal-sub { font-size: 0.92rem; color: var(--wt-ink-2); margin: 0 0 24px; }
.inquiry-field { display: block; margin-bottom: 14px; }
.inquiry-field span { display: block; font-size: 0.8rem; font-weight: 600; color: var(--wt-ink-2); margin-bottom: 6px; }
.inquiry-field input, .inquiry-field textarea {
	width: 100%; box-sizing: border-box; padding: 10px 13px; border-radius: 8px;
	border: 1px solid var(--wt-border); background: var(--wt-surface);
	font-family: 'Inter', sans-serif; font-size: 0.95rem; color: var(--wt-ink);
	transition: border-color 0.15s ease;
}
.inquiry-field textarea { resize: vertical; min-height: 80px; font-family: inherit; }
.inquiry-field input:focus, .inquiry-field textarea:focus { border-color: var(--wt-cyan); outline: none; }
.inquiry-modal-submit {
	width: 100%; margin-top: 6px; padding: 12px 14px; border: none; border-radius: 8px;
	background: var(--wt-navy); color: #fff; font-family: 'Space Grotesk', sans-serif; font-weight: 700; font-size: 0.92rem;
	cursor: pointer; transition: background 0.15s ease;
}
.inquiry-modal-submit:hover { background: var(--wt-navy-2); }
.inquiry-modal-submit:disabled { opacity: 0.6; cursor: default; }
.inquiry-modal-note { font-size: 0.78rem; color: var(--wt-ink-3); margin: 12px 0 0; line-height: 1.5; }
.inquiry-modal-error { font-size: 0.82rem; color: #b3261e; margin: 12px 0 0; }
.inquiry-modal-success-title { font-family: 'Space Grotesk', sans-serif; font-weight: 700; font-size: 1.15rem; margin: 0 0 8px; color: var(--wt-ink); }
.inquiry-modal-success-body { font-size: 0.95rem; color: var(--wt-ink-2); margin: 0 0 24px; line-height: 1.5; }

/* =========================================================================
   ABOUT US — design-consistency pass (25 Aug 2026)
   First step of bringing the homepage's Navy-Dominant type system (see the
   "HOMEPAGE REVAMP" block above) to the rest of the site, one page at a
   time, per Soana's request. Scoped to `.page-about-us` (new body class,
   see wantok_body_classes() in functions.php) rather than the shared
   .richtext-section/.feature-cards-section classes directly, since those
   are reused by Support/Promotions/other pages that haven't had their own
   consistency pass yet — this keeps today's change from silently bleeding
   onto pages nobody has reviewed against the new look. Same reasoning as
   the homepage rule's own comment on why .cta-band was left out.
   ========================================================================= */
.page-about-us .about-fan-hero,
.page-about-us .richtext-section,
.page-about-us .feature-cards-section {
	--wt-font-display: 'Manrope', sans-serif;
	--wt-font-body: 'Manrope', sans-serif;
}
/* "Data chip" labels — same treatment as the homepage's
   .trust-inline-detail / .latest-news .news-date — small mono caps for a
   label riding on top of a photo or beside a name, not body copy. */
.page-about-us .feature-mosaic-tile-tag,
.page-about-us .about-fan-photo-label-role {
	font-family: var(--wt-font-mono);
}

/* Follow-up (25 Aug 2026): the full-navy band above didn't land — Soana
   asked to go back to a white section but still "include navy onto it".
   Reading: white background, navy carried as an accent rather than a
   fill — the heading in navy (rather than the site's default ink colour).
   Explicit var(--wt-bg) rather than the base `.richtext-band` rule's pale
   surface tint, since "white" was said outright. The icon mark's own
   navy/cyan colouring is set directly in its wantok_duo_icon() call
   (page-content-render.php) rather than here — see the note above the
   .richtext-signal-icon rule for that swap. */
.page-about-us .richtext-band { background: var(--wt-bg); }
.page-about-us .richtext-band h2 { color: var(--wt-navy); }

/* =========================================================================
   SERVICES AND PLANS — design-consistency pass (25 Aug 2026)
   Second page of the page-by-page Navy-Dominant rollout (see the ABOUT US
   block above and the "HOMEPAGE REVAMP" block earlier in this file).
   Scoped to `.page-services-and-plans` (body class from
   wantok_body_classes() in functions.php) rather than the shared
   `.services-showcase-section`/`.services-showcase-card` classes directly —
   same reasoning as the About Us block: those classes currently only
   render on this one page, but scoping keeps that true on purpose rather
   than by accident if a future page ever reuses the showcase-card markup.

   Two things asked for this round, decided with Soana after finding that
   the 7 Aug "Tide Bands" navy background band (behind grid row 2) is no
   longer present in the live stylesheet — most likely dropped when the
   24-25 Aug homepage-revamp merge replaced this file from an older
   baseline zip that predated it. Rather than resurrect a background
   treatment from before the Navy-Dominant language existed, Soana chose to
   leave the canvas white and bring the *current* accent language (the
   coloured ring-glow used on .plans .plan-card and the About Us
   feature-mosaic-tile) to these cards instead — one consistent navy
   treatment site-wide rather than two competing ones. If Tide Bands is
   wanted back later, treat it as a fresh request, not a restore.

   1. Typography — Manrope for the card grid only, NOT the
      .services-reel-hero above it. The reel hero's eyebrow/title were
      already deliberately restyled (11 August) to match the homepage's
      own .hero-eyebrow/.hero-title, and the homepage rule (see
      "HOMEPAGE REVAMP" above) leaves that hero on the original Space
      Grotesk/Inter on purpose — keeping this page's reel hero on the same
      footing avoids a font seam between two things that already match
      each other. */
.page-services-and-plans .services-showcase-section {
	--wt-font-display: 'Manrope', sans-serif;
	--wt-font-body: 'Manrope', sans-serif;
}

/* 2. Card accents — alternating cyan/purple ring-glow per card (glow class
   set in wantok_get_services_showcase()/wantok_block_services_showcase(),
   functions.php + page-content-render.php), same coloured-glow language as
   .plans .plan-card.glow-cyan/-purple and .feature-mosaic-tile.glow-cyan/
   -purple above: a tinted border plus a soft blurred halo, intensifying on
   hover. Applies to the wide Innovation Hub card too, since
   .services-showcase-card--wide inherits the base .services-showcase-card
   border/box-shadow rather than overriding them. */
.services-showcase-card.glow-cyan {
	border-color: color-mix(in srgb, var(--wt-cyan) 45%, transparent);
	box-shadow: 0 18px 40px rgba(12,19,48,0.1), 0 0 26px color-mix(in srgb, var(--wt-cyan) 20%, transparent);
}
.services-showcase-card.glow-purple {
	border-color: color-mix(in srgb, var(--wt-purple) 45%, transparent);
	box-shadow: 0 18px 40px rgba(12,19,48,0.1), 0 0 26px color-mix(in srgb, var(--wt-purple) 20%, transparent);
}
.services-showcase-card.glow-cyan:hover {
	border-color: color-mix(in srgb, var(--wt-cyan) 70%, transparent);
	box-shadow: var(--wt-shadow-lg), 0 0 38px color-mix(in srgb, var(--wt-cyan) 42%, transparent);
}
.services-showcase-card.glow-purple:hover {
	border-color: color-mix(in srgb, var(--wt-purple) 70%, transparent);
	box-shadow: var(--wt-shadow-lg), 0 0 38px color-mix(in srgb, var(--wt-purple) 42%, transparent);
}

/* =========================================================================
   SERVICES AND PLANS — AI Implementation card + even layout
   (25 Aug 2026, later same day as the design-consistency pass above)
   ========================================================================= */

/* Icon-badge placeholder for a card with no mascot cutout yet (see the
   'icon' branch in wantok_block_services_showcase(), page-content-
   render.php) — sits in the same 0-260px slot the floating photo box
   would otherwise occupy (.services-showcase-body's own margin-top:260px
   is unchanged, so title/desc land in the same place either way), but
   centred and contained within the card rather than floating ~46px above
   it like the photo cards — there's no cutout silhouette here to justify
   the overlap, and floating a plain circle that high looked like a
   mistake rather than a design choice. Reuses .service-icon's badge
   language (56px circle, --wt-bg fill, soft shadow) from the homepage's
   own icon-card system, sized up slightly (80px) to read at this card's
   larger scale. */
.services-showcase-icon-slot {
	position: absolute; top: 0; left: 0; right: 0; height: 260px;
	display: flex; align-items: center; justify-content: center;
}
.services-showcase-icon-badge {
	display: flex; align-items: center; justify-content: center;
	width: 80px; height: 80px; border-radius: 50%;
	background: var(--wt-bg); color: var(--wt-navy);
	box-shadow: 0 1px 2px rgba(28,44,104,0.08), 0 14px 30px rgba(12,19,48,0.1);
}

/* =========================================================================
   SERVICES AND PLANS — compact card grid ("fit all the cards on screen")
   (25 Aug 2026, same day as the layout pass above)
   Soana picked Option 1 (1180px, 4 columns) from the width comparison,
   then asked: "i want the cards to all fit on the screen." The base
   .services-showcase-* rules above are ~62% of their original size here
   (card 480px → 336px tall, photo box 272px → 168px, etc.) so the full
   2-row/8-card grid fits within a single ~900px-tall viewport once
   scrolled into view, instead of needing to scroll through 1000px+ of
   card grid alone. This does NOT also shrink the video reel-hero banner
   above the grid (.services-reel-hero, untouched) — Soana's ask was
   specifically about "the cards", and that hero is a separate, larger
   component shared with the request-a-quote pages; if the whole page
   needs to fit one screen including the hero, that's a separate,
   bigger change worth asking about on its own.

   Scoped to `.page-services-and-plans` rather than edited in place on
   the base rules above, same reasoning as the rest of this file's
   page-specific passes: those base rules are shared class names that
   currently only render on this one page, and scoping keeps that true
   on purpose rather than by accident if a future page ever reuses this
   card markup at its original size. */
/* 27 Aug — Soana asked for the homepage's "What We Do" cards to match this
   compact sizing (and picked up the same fixed-height alignment fix below
   for free, since it's the same rules) — extended every selector in this
   block to also match `.wantok-front` (the homepage's own body class, see
   wantok_body_classes() in functions.php), rather than duplicating the
   ruleset. Deliberately NOT extending `.services-showcase-section`'s own
   padding (further down) or the Manrope font-family override above —
   Soana's ask was specifically about card sizing/alignment, and the
   homepage's own section spacing/typography is an unrelated, separately-
   tuned decision. */
.page-services-and-plans .services-showcase-grid,
.wantok-front .services-showcase-grid { gap: 40px 24px; }
/* 26 Aug (Round 11) — card height bumped 300px -> 336px as part of the
   title/desc alignment fix directly below. See that comment block for the
   full reasoning; this taller card is what gives the now-fixed-height
   title + description room to sit without either overflowing upward into
   the photo box or leaving the arrow button to bounce around card to card. */
.page-services-and-plans .services-showcase-card,
.wantok-front .services-showcase-card { height: 336px; }
.page-services-and-plans .services-showcase-photo,
.wantok-front .services-showcase-photo { top: -28px; height: 168px; }
.page-services-and-plans .services-showcase-icon-slot,
.wantok-front .services-showcase-icon-slot { height: 160px; }
.page-services-and-plans .services-showcase-icon-badge,
.wantok-front .services-showcase-icon-badge { width: 64px; height: 64px; }
.page-services-and-plans .services-showcase-body,
.wantok-front .services-showcase-body { margin-top: 160px; padding: 0 18px 18px; }
/* 26 Aug (Round 11) — "the text in the cards are not all aligned with each
   other." Root cause: .services-showcase-card bottom-anchors its one flex
   child (.services-showcase-body, see the base .services-showcase-card
   rule near the top of this file) via justify-content: flex-end, so the
   button always lands flush — but that only holds the BOTTOM of the text
   block steady. The title runs 1 or 2 lines depending on its own length
   (e.g. "Business Solutions" vs "Utility Management System (UMS)"), and
   the description runs anywhere from 2 to 4 lines depending on both its
   own length and the current breakpoint's card width — so with a variable-
   height block anchored from the bottom, the TOP of that block (where the
   title starts) lands at a different y on almost every card. Measured this
   directly (title_y varied by ~20px within a single row) before touching
   anything, rather than guessing at the cause.
   Fix: give the title and description each a fixed-height box, so every
   card's text block is an identical height regardless of its own copy —
   title reserves 2 lines (min-height: 2.3em at line-height 1.15 = this
   card's actual 2-line height, confirmed against the UMS/AI Implementation
   cards' measured 2-line titles), description clamps to 3 lines
   (line-height 1.55) with an ellipsis for the rare case that runs past
   that (only Innovation Hub's description does, and only at the ~981-
   1024px width band — every other card's copy fits inside 3 lines at every
   width, so this trims at most a few trailing words in one place, not a
   rewrite). With both boxes now a constant height, the flex-end packing
   above places the title, description, and arrow at the exact same y on
   every one of the 8 cards — not just flush within a row, which is as far
   as the Round 7 reorder's own alignment check went, but flush across the
   whole grid. Card height (300px -> 336px above) was bumped to give this
   fixed-height stack room to sit without pushing up into the photo box the
   way the old variable-height content sometimes did. */
body.wp-theme-wantok-new.page-services-and-plans .services-showcase-title,
body.wp-theme-wantok-new.wantok-front .services-showcase-title {
	font-size: 1.05rem; line-height: 1.15; margin: 0 0 6px; min-height: 2.3em;
}
.page-services-and-plans .services-showcase-desc,
.wantok-front .services-showcase-desc {
	font-size: 0.82rem; margin: 0 0 12px;
	display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
	overflow: hidden;
	/* min-height AND max-height, not max-height alone: line-clamp only caps
	   overflow, it doesn't pad a short (1-2 line) description up to the
	   full 3 lines on its own — a max-height-only version left this box's
	   real height varying with content after all, which (via the card's
	   flex-end packing) shifted the WHOLE text block, title included,
	   right back into the same misalignment this pass exists to fix.
	   min-height forces every description to occupy exactly 3 lines of
	   space regardless of how short the copy actually is. */
	min-height: 4.65em; max-height: 4.65em;
}
.page-services-and-plans .services-showcase-arrow,
.wantok-front .services-showcase-arrow { width: 36px; height: 36px; }
.page-services-and-plans .services-showcase-arrow svg,
.wantok-front .services-showcase-arrow svg { width: 16px; height: 16px; }
/* Bottom padding bumped 36px → 64px (26 Aug, "put some little space between
   the cards, contact card and how we work section") — at the compact card
   height the grid's last row sat almost flush against the contact band
   below it; this restores a visible breathing gap without re-inflating the
   grid itself. See the matching bump on .contact-band-section's own bottom
   padding below for the gap on its other side. */
.page-services-and-plans .services-showcase-section { padding: 56px 0 64px; }
/* Recalibrated for the new, shorter card — see the base
   .services-showcase-photo--banner img rule's comment for why this shift
   exists at all. Re-measured empirically against the new dimensions
   rather than just scaling the old 60px figure down proportionally.
   Worst case measured by comparing the actual rendered boxes (photo box
   bottom edge vs body box top edge, not just the margin-top arithmetic,
   which undercounts once the flex packing compresses) across widths
   390–1440px: Innovation Hub's card (index 7) overlaps by up to 34px at
   981px viewport width. Matching the ~14px safety margin the base
   60px-for-~46px figure above used, 50px clears this worst case with
   room to spare. */
.page-services-and-plans .services-showcase-photo--banner img,
.wantok-front .services-showcase-photo--banner img { object-position: center calc(100% - 50px); }

/* =========================================================================
   SERVICES AND PLANS — contact band + process steps (25 Aug 2026, layout
   pass). New sections below the card grid, built from the reference layout
   Soana shared (an "Agence" template Services page: 4x2 card grid, a
   phone-number CTA band, a 3-step "Process We Follow" strip). Markup is
   wantok_block_contact_band()/wantok_block_process_steps() in page-
   content-render.php — see the doc comment above those functions for why
   they're new block types rather than reusing .cta-band/.hiw-*. Visual
   language matches the rest of this Navy-Dominant pass: white canvas,
   navy ink, cyan/purple as the only saturated colour, IBM Plex Mono for
   small caption labels (same treatment as .trust-inline-detail /
   .feature-mosaic-tile-tag), Manrope for headings (scoped above). */

/* ---- Contact band ---- */
/* Bottom padding bumped 8px → 40px (26 Aug, same "little space" request as
   the .services-showcase-section comment above) — gives the band its own
   clear gap above "How We Work" rather than relying only on that section's
   96px top padding to carry the whole gap. */
.contact-band-section { padding: 0 0 40px; }
/* 25 August (2nd follow-up) — Soana asked to make the whole band bigger
   ("this is really good, please make the contact band bigger"). Scaled
   up padding/gap, the icon circle, and every text/button size together
   (not just padding alone) so it reads as a genuinely larger element
   rather than the same content sitting in more whitespace. Navy fill
   matching the header/utility-bar (var(--wt-navy), same token, not the
   CTA band's own --wt-navy-grad — Soana specifically said "match how the
   headers are navy") plus a two-sided cyan/purple ambient halo instead of
   the card grid's single-colour glow. The border-radius + gradient-border
   combo needs the two-background-layers trick (border-image ignores
   border-radius and would square off the corners): a solid navy fill
   painted into the padding-box, the gradient painted into the border-box
   behind it, so the gradient only shows through the border's own width. */
.contact-band {
	display: flex; align-items: center; gap: 36px;
	position: relative; color: var(--wt-on-dark);
	border-radius: 24px; padding: 46px 56px;
	border: 1.5px solid transparent; background-clip: padding-box, border-box;
	background-origin: padding-box, border-box;
	background-image: linear-gradient(var(--wt-navy), var(--wt-navy)), var(--wt-grad);
	box-shadow:
		0 20px 50px rgba(12,19,48,0.4),
		-20px 0 56px color-mix(in srgb, var(--wt-cyan) 32%, transparent),
		20px 0 56px color-mix(in srgb, var(--wt-purple) 32%, transparent);
}
.contact-band-icon {
	flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
	width: 80px; height: 80px; border-radius: 50%;
	background: var(--wt-bg); color: var(--wt-navy);
	box-shadow: 0 1px 2px rgba(0,0,0,0.2), 0 14px 30px rgba(0,0,0,0.3);
}
.contact-band-text { flex: 0 0 auto; }
.contact-band-label {
	font-family: var(--wt-font-mono); font-size: 0.82rem; letter-spacing: 0.08em;
	text-transform: uppercase; color: var(--wt-cyan); margin: 0 0 6px;
}
.contact-band-number {
	font-family: var(--wt-font-display); font-weight: 700; font-size: 1.9rem;
	letter-spacing: -0.01em; color: #fff;
}
.contact-band-number:hover { color: var(--wt-cyan); }
.contact-band-divider { width: 1px; align-self: stretch; background: rgba(255,255,255,0.2); flex: 0 0 auto; }
.contact-band-copy { flex: 1 1 300px; margin: 0; color: var(--wt-on-dark-2); font-size: 1.05rem; line-height: 1.55; }
/* 25 August (follow-up) — base .btn's `border: 2px solid transparent`
   (there for .btn-outline's benefit) sits on top of .btn-gradient's fill
   and can show as a faint seam at a 999px pill's curve, so it's set to
   `none` here rather than inherited unused; padding switched from em to a
   flat rem value plus an explicit line-height:1 so the pill's height (and
   its corner radius) lands on a clean pixel number regardless of font
   metrics. */
.contact-band-btn {
	flex: 0 0 auto; white-space: nowrap; font-size: 1.05rem;
	padding: 1rem 1.9rem; line-height: 1; border: none;
}
@media (max-width: 860px) {
	.contact-band { flex-wrap: wrap; }
	.contact-band-divider { display: none; }
	.contact-band-copy { flex-basis: 100%; order: 3; }
	.contact-band-btn { order: 4; }
}
@media (max-width: 560px) {
	.contact-band { flex-direction: column; align-items: flex-start; padding: 34px 28px; gap: 22px; }
	.contact-band-btn { width: 100%; text-align: center; }
}

/* ---- Process we follow ---- */
.process-steps-section { padding: 96px 0 8px; }
.process-steps-head { max-width: 640px; margin: 0 auto 3.2rem; text-align: center; }
.process-steps-eyebrow {
	display: block; font-family: var(--wt-font-mono); font-size: 0.78rem;
	letter-spacing: 0.08em; text-transform: uppercase; color: var(--wt-blue);
	margin-bottom: 0.7rem;
}
.process-steps-head h2 { font-size: clamp(1.8rem, 3vw, 2.3rem); color: var(--wt-navy); }
.process-steps-intro { color: var(--wt-ink-2); font-size: 1.02rem; margin: 0.6rem 0 0; }
/* 3 columns with a dashed connector running behind the circles — the
   connector is a single pseudo-element line, not per-step segments, so it
   reads as one continuous thread rather than 2 disjointed dashes. */
.process-steps-grid { position: relative; display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; }
.process-steps-grid::before {
	content: ''; position: absolute; left: calc(100% / 6); right: calc(100% / 6);
	top: 34px; height: 0; border-top: 2px dashed var(--wt-border); z-index: 0;
}
.process-step { position: relative; z-index: 1; text-align: center; }
.process-step-circle {
	width: 68px; height: 68px; margin: 0 auto 1.1rem; border-radius: 50%;
	display: flex; align-items: center; justify-content: center;
	background: var(--wt-bg); border: 1.5px solid var(--wt-border);
	box-shadow: 0 1px 2px rgba(28,44,104,0.08), 0 14px 30px rgba(12,19,48,0.1);
}
.process-step-num {
	font-family: var(--wt-font-mono); font-size: 1.05rem; font-weight: 600; color: var(--wt-navy);
}
/* Alternate the circle's ring colour cyan/purple, same coloured-glow
   language as the card grid above (.services-showcase-card.glow-*) rather
   than leaving every circle the same flat grey — ties this section back
   into the page's one accent language instead of reading as unrelated. */
.process-step:nth-child(odd) .process-step-circle { border-color: color-mix(in srgb, var(--wt-cyan) 55%, transparent); }
.process-step:nth-child(even) .process-step-circle { border-color: color-mix(in srgb, var(--wt-purple) 55%, transparent); }
.process-step-title { font-size: 1.05rem; margin: 0 0 0.5rem; color: var(--wt-ink); }
.process-step-copy { margin: 0 auto; max-width: 220px; font-size: 0.88rem; color: var(--wt-ink-2); line-height: 1.5; }
@media (max-width: 780px) {
	.process-steps-grid { grid-template-columns: 1fr; gap: 44px; max-width: 320px; margin: 0 auto; }
	.process-steps-grid::before { display: none; }
}

/* 27 August — the orphan-centring hack that used to live here
   (hardcoded to the old 7-card :nth-child(7)) no longer applies: the
   desktop grid moved from 3 to 4 columns the same day Innovation Hub —
   Tonga switched from a full-span `wide` card to a regular one (see
   .services-showcase-grid and wantok_get_services_showcase() above/in
   functions.php), so all 8 cards now divide evenly into two full rows
   of 4 with nothing left over. Removed rather than left dormant. If a
   9th card is ever added, re-check whether a similar hack is needed
   again. */


/* =========================================================================
   INNOVATION HUB — TONGA (26 August 2026)
   New page at /innovation-hub-tonga/ — the destination the homepage/
   Services and Plans "Innovation Hub — Tonga" wide card's arrow already
   linked to (wantok_get_services_showcase()) before this page existed.
   Built from innovation-hub-option-b-white.html (Option B / white — a
   full-photo banner using the existing Samsung Innovation Hub banner
   graphic, no navy fill elsewhere on the page), plus a later mosaic pass
   on its closing photo gallery.
   ========================================================================= */

/* 1. Static-image sibling to .page-title-banner-video-bg (see
   wantok_banner_image_bg() in page-content-render.php) — same layer/crop
   treatment as the video version, just an <img> instead of a <video>. */
.page-title-banner-image-bg {
	position: absolute; inset: 0; z-index: 0; overflow: hidden;
	background: var(--wt-navy-grad);
}
.page-title-banner-image-bg img {
	position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}

/* 2. 28 August 2026 — the banner photo this page used (with its own
   baked-in "Step into the future. Samsung Innovation Hub." headline) is
   gone; the visual hero is now the "walk through the space" viewer,
   filling this banner edge-to-edge as its full-bleed background (see
   wantok_banner_hub_viewer_bg()/wantok_block_hub_viewer()'s '_hero' mode,
   page-content-render.php). Later the same day, per Soana: the real
   eyebrow/title/intro are NOT hidden any more (no 'banner_hide_text') —
   they float on top of the viewer instead, same as Broadband Plans'
   video hero floats its text over footage; see
   .page-title-banner--hub-viewer-hero just below, which mirrors
   .page-title-banner--video-hero/.page-title-banner-scrim's existing
   background-layer + scrim + floating-text structure. */
.page-title-banner-hub-viewer-bg {
	position: absolute; inset: 0; z-index: 0; overflow: hidden;
	background: var(--wt-navy-grad);
}
/* Text anchored to the top rather than centred over the whole banner —
   the viewer has its own real UI at the very top (zoom controls, centred
   hint) and bottom (prev/next arrows, dots) that needs to stay clear of
   the eyebrow/title/intro column; same reasoning/measurements as the
   iframe-embed prototype this replaces (see
   wantok_innovation_hub_walkthrough_hero_27aug.md) — clamp() floor keeps
   the banner from going below the viewer's own real rendered height
   regardless of viewport height, a short viewport means scrolling to see
   the whole hero rather than silently clipping the viewer's controls. */
.page-title-banner--hub-viewer-hero {
	min-height: clamp(830px, 82vh, 900px);
	align-items: flex-start;
}
.page-title-banner--hub-viewer-hero .page-title-banner-inner {
	padding-top: 100px;
}
@media (max-width: 700px) {
	.page-title-banner--hub-viewer-hero { min-height: min(92vh, 780px); }
	.page-title-banner--hub-viewer-hero .page-title-banner-inner { padding-top: 84px; }
}
/* The viewer itself (.hub-viewer--hero, added by wantok_block_hub_
   viewer()'s '_hero' mode) needs to fill this whole banner edge-to-edge
   with no rounded-rectangle box, and drop the overlay text that would
   otherwise duplicate/collide with the eyebrow/title/intro above — see
   the "5." block further below for that. */

/* 3. 28 August 2026 — this page's banner is now plain navy with no photo,
   sitting directly above the dark .hub-viewer rather than the light
   .band-tint info-band the wave used to match. 'banner_wave_surface'
   (page-content-data.php) still applies here, just repointed from the old
   #f4f6fb tint to the viewer's own near-black ground (--hv-bg in
   style.css's INNOVATION HUB — TONGA: "walk through the space" viewer
   block) so the wave reads as one continuous dark surface instead of
   dipping back to white for a beat before the viewer starts. */
.page-innovation-hub-tonga .page-title-banner-wave--surface path { fill: #161826; }

/* 4. "Book a Visit" info band — three quiet icon labels (Hours/Capacity/
   Rates) naming what's covered, then a single CTA out to the booking
   partner's own site (samsunghub.to), which stays the one source of truth
   for those specifics rather than this page restating figures that can
   drift out of date. See wantok_block_info_band() in
   page-content-render.php. */
.info-band { text-align: center; }
.info-band-inner { max-width: 700px; }
.info-band-highlights { display: flex; justify-content: center; gap: 44px; margin: 14px 0 36px; flex-wrap: wrap; }
.info-band-hl { display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--wt-ink-2); font-size: 0.82rem; font-weight: 600; }
/* 27 August — real bug fix, found while verifying the gallery-removal
   change on this same page: this rule only set `color`, on the
   assumption the icon's own <svg> markup carried inline
   `fill="none" stroke="currentColor"` (true of the hand-built mockup
   this page was designed from, but NOT true of the shared
   wantok_svc_icon() helper that actually renders these icons — its
   `<svg viewBox="0 0 24 24">` wrapper sets neither). Every OTHER caller
   of that helper works around this with its own fill:none/stroke rule
   (see .svc-card-icon svg / .hiw-icon svg elsewhere in this file) — this
   one was missed, so the Hours/Capacity/Rates icons here were rendering
   as solid black shapes (SVG's fill defaults to black, stroke to none)
   instead of the intended blue outline icons, since 26 August. */
.info-band-hl svg { fill: none; stroke: var(--wt-blue); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.info-band-btn { display: inline-flex; padding: 1.05em 2.3em; font-size: 1.05rem; }
.info-band .band-eyebrow { font-size: 0.86rem; }
.info-band .band-title { font-size: clamp(2.1rem, 3.6vw, 2.7rem); margin: 0 0 16px; }
.info-band .band-lead { font-size: 1.08rem; max-width: 560px; margin: 0 auto; }
/* "Book a Visit" as the page's clear focal point — bigger vertical padding
   than the Samsung Products section below it; that section's own padding
   trimmed slightly (scoped to this page only, since .band-light.pc-section
   is shared with Business Solutions Plans/Cybersecurity Plans and neither
   asked for a different rhythm) so it reads as the secondary section it
   is. */
.page-innovation-hub-tonga .band-tint.info-band { padding: 112px 0 116px; }
.page-innovation-hub-tonga .band-light.pc-section { padding: 56px 0 52px; }
@media (max-width: 700px) {
	.page-innovation-hub-tonga .band-tint.info-band { padding: 72px 0 76px; }
}

/* 5. "From Past Events at the Hub" — mosaic layout (see the 'mosaic' flag
   on wantok_block_gallery() in page-content-render.php), replacing the
   plain 3-column .team-gallery grid for this one section only (still
   reusing .team-gallery-item's own tile styling — radius, shadow, hover
   lift, object-fit — just changing how the tiles are arranged). 5 photos
   in a straight 3-column grid left an orphaned row of 2; one photo
   featured at double width/full height plus a 2x2 grid beside it gives a
   balanced five-photo layout with no leftover row. */
.event-gallery-mosaic {
	display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(2, 1fr);
	height: 560px;
}
.event-gallery-mosaic .team-gallery-item { aspect-ratio: auto; height: 100%; }
.event-gallery-featured { grid-column: 1 / 3; grid-row: 1 / 3; }
@media (min-width: 701px) and (max-width: 900px) {
	.event-gallery-mosaic { height: 460px; }
}
@media (max-width: 700px) {
	.event-gallery-mosaic {
		grid-template-columns: 1fr; grid-template-rows: none; height: auto; gap: 1.1rem;
	}
	.event-gallery-mosaic .team-gallery-item { aspect-ratio: 4 / 3; height: auto; }
	.event-gallery-featured { grid-column: auto; grid-row: auto; }
}

/* =========================================================================
   ANKER SOLIX — SPOTLIGHT SELECTOR (28 August 2026)
   Anker Solix page — replaces the 6 separate stacked compact-richtext
   product cards (5/27 August round) with one interactive tabbed unit:
   model tabs across the top and a capacity "step track" along the
   bottom, both driving the same active panel in between (photo, spec
   badges, description, short checklist). Ported from anker-preview.html
   (Soana's reference build); markup/JS live in
   wantok_block_spotlight_selector(), page-content-render.php. Uses the
   theme's own --wt- tokens throughout rather than one-off colours, so it
   inherits any future brand-colour change for free.
   ========================================================================= */
.anker-spotlight-embed { margin: 8px auto 0; }
.anker-spotlight-embed.container-wide { max-width: 1400px; }
.spotlight-shell { background: var(--wt-navy); border-radius: 24px; overflow: hidden; box-shadow: var(--wt-shadow-lg); }
.spotlight-tabs { display: flex; overflow-x: auto; gap: 6px; padding: 18px 20px 0; scrollbar-width: none; }
.spotlight-tabs::-webkit-scrollbar { display: none; }
.spotlight-tab {
	appearance: none; border: none; background: transparent; color: var(--wt-on-dark-2);
	font-family: var(--wt-font-display); font-weight: 600; font-size: 0.82rem;
	padding: 11px 18px; border-radius: 12px 12px 0 0; cursor: pointer; white-space: nowrap;
	transition: all 0.2s ease;
}
.spotlight-tab:hover { color: #fff; }
.spotlight-tab.is-active { background: var(--wt-surface); color: var(--wt-navy); }
.spotlight-stage { background: var(--wt-surface); padding: 0; }
.spotlight-panel { display: none; }
.spotlight-panel.is-active { display: grid; grid-template-columns: 0.8fr 1.2fr; align-items: center; gap: 0; }
.spotlight-media { padding: 26px 18px; display: flex; align-items: center; justify-content: center; position: relative; min-height: 340px; }
.spotlight-media-glow { position: absolute; inset: 10%; border-radius: 50%; filter: blur(50px); opacity: 0.35; z-index: 0; }
.spotlight-panel[data-glow="cyan"] .spotlight-media-glow { background: var(--wt-cyan); }
.spotlight-panel[data-glow="purple"] .spotlight-media-glow { background: var(--wt-purple); }
.spotlight-media img { position: relative; z-index: 1; max-height: 340px; max-width: 100%; }
.spotlight-copy { padding: 40px 44px 40px 0; }
.spotlight-eyebrow { font-family: var(--wt-font-display); font-weight: 600; font-size: 0.76rem; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 8px; }
.spotlight-panel[data-glow="cyan"] .spotlight-eyebrow { color: var(--wt-blue); }
.spotlight-panel[data-glow="purple"] .spotlight-eyebrow { color: var(--wt-purple); }
.spotlight-name { font-size: 1.7rem; color: var(--wt-navy); margin-bottom: 10px; font-family: var(--wt-font-display); }
.spotlight-specline { display: flex; gap: 10px; margin-bottom: 16px; }
.spotlight-spec { font-family: var(--wt-font-display); font-weight: 700; font-size: 0.95rem; color: var(--wt-navy); background: #fff; border: 1px solid var(--wt-border); padding: 7px 14px; border-radius: 10px; }
.spotlight-desc { color: var(--wt-ink-2); font-size: 0.92rem; line-height: 1.6; margin-bottom: 18px; }
.spotlight-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 9px 16px; }
.spotlight-list li { font-size: 0.83rem; color: var(--wt-ink-2); padding-left: 18px; position: relative; line-height: 1.4; }
.spotlight-list li::before { content: "✓"; position: absolute; left: 0; top: 0; font-weight: 700; color: var(--wt-cyan); }

.step-track { position: relative; display: flex; justify-content: space-between; padding: 30px 40px 26px; background: var(--wt-surface); border-top: 1px solid var(--wt-border); }
.step-line { position: absolute; left: 52px; right: 52px; top: 47px; height: 2px; background: var(--wt-border); }
.step-node-wrap { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 7px; cursor: pointer; flex: 1; }
.step-node { width: 14px; height: 14px; border-radius: 50%; background: #fff; border: 2px solid var(--wt-border); transition: all 0.2s ease; }
.step-node-wrap.is-active .step-node { width: 20px; height: 20px; border-color: transparent; background: var(--wt-grad); box-shadow: 0 0 0 5px color-mix(in srgb, var(--wt-cyan) 16%, transparent); }
.step-node-wrap:hover .step-node { border-color: var(--wt-blue); }
.step-label { font-family: var(--wt-font-display); font-size: 0.7rem; font-weight: 600; color: var(--wt-ink-3); text-align: center; }
.step-node-wrap.is-active .step-label { color: var(--wt-navy); }
.step-sub { font-size: 0.64rem; color: var(--wt-ink-3); }

@media (max-width: 980px) {
	.spotlight-panel.is-active { grid-template-columns: 1fr; }
	.spotlight-copy { padding: 8px 32px 40px; }
	.step-track { flex-wrap: wrap; }
	.step-line { display: none; }
}

/* =========================================================================
   INNOVATION HUB — TONGA: "walk through the space" viewer (28 August 2026)
   Replaces the page's static full-bleed banner photo as its real visual
   hero — see wantok_block_hub_viewer(), page-content-render.php, for the
   full history/porting notes. Deliberately its own small self-contained
   dark colour system (--hv-* custom properties below) rather than the
   site's --wt- tokens: this component's design was approved as its own
   distinct look (a near-black indigo, not quite the site's --wt-navy),
   and keeping it scoped means retuning the main theme's palette later
   can't accidentally shift this component too.
   ========================================================================= */
.hub-viewer {
	--hv-bg: #161826;
	--hv-bg-2: #1d2036;
	--hv-surface: #232532;
	--hv-text: #e9e9ed;
	--hv-accent: #9184d9;
	--hv-accent-300: #d2cefd;
	--hv-accent-600: #796cbf;
	--hv-neutral-100: #f3f5fe;
	--hv-neutral-200: #e4e7f5;
	--hv-neutral-300: #cfd3e5;
	--hv-neutral-400: #b2b6ca;
	--hv-neutral-700: #595d6c;
	background: radial-gradient(120% 80% at 20% 0%, var(--hv-bg-2) 0%, var(--hv-bg) 60%);
	color: var(--hv-text);
	padding: 32px 0 40px;
}
.hub-viewer-inner { display: flex; flex-direction: column; gap: 16px; }

.hub-viewer-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.hub-viewer-head-text { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.hub-viewer-kicker { display: flex; align-items: center; gap: 10px; }
.hub-viewer-kicker-line { width: 26px; height: 1px; background: var(--hv-accent); }
.hub-viewer-kicker span:last-child { font-size: 10.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--hv-neutral-300); }
.hub-viewer-title { font-family: var(--wt-font-display); font-weight: 500; font-size: clamp(26px, 5.2vw, 40px); line-height: 1.08; letter-spacing: -0.02em; }
.hub-viewer-lead { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--hv-neutral-300); max-width: 56ch; }
.hub-viewer-counter { font-family: var(--wt-font-display); font-size: 12px; letter-spacing: 0.1em; color: var(--hv-neutral-400); white-space: nowrap; }

.hub-viewer-stage {
	position: relative; width: 100%; aspect-ratio: 16 / 9; border-radius: 8px; overflow: hidden;
	border: 1px solid var(--hv-neutral-700); background: #0f1120; box-shadow: 0 0 0 1px var(--hv-neutral-300), 0 16px 40px rgba(0,0,0,0.65);
	touch-action: none; cursor: grab; user-select: none; -webkit-user-select: none;
}
.hub-viewer-track { display: flex; height: 100%; width: 100%; will-change: transform; }
.hub-viewer-frame { flex: 0 0 100%; height: 100%; position: relative; overflow: hidden; }
.hub-viewer-frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; will-change: transform; }
.hub-viewer-scrim { position: absolute; inset: 0; pointer-events: none; background: linear-gradient(rgba(15,17,32,.55) 0%, rgba(15,17,32,0) 26%, rgba(15,17,32,0) 52%, rgba(15,17,32,.82) 100%); }

.hub-viewer-round {
	appearance: none; display: grid; place-items: center; width: 40px; height: 40px; padding: 0;
	font-family: var(--wt-font-display); font-size: 18px; line-height: 1; border-radius: 999px;
	border: 1px solid var(--hv-neutral-700); background: rgba(22,24,38,.62); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
	color: var(--hv-text); cursor: pointer; opacity: 1;
	transition: background .18s ease, border-color .18s ease, opacity .18s ease;
}
.hub-viewer-round:hover { border-color: var(--hv-accent); background: rgba(145,132,217,.2); }
.hub-viewer-round.is-disabled { color: var(--hv-neutral-400); opacity: 0.45; cursor: default; pointer-events: none; }
.hub-viewer-round--lg { width: 46px; height: 46px; font-size: 24px; }

.hub-viewer-zoom-controls { position: absolute; top: 14px; right: 14px; display: flex; flex-direction: column; gap: 8px; z-index: 3; }
.hub-viewer-zoom-badge {
	text-align: center; font-family: var(--wt-font-display); font-size: 10.5px; letter-spacing: 0.06em;
	padding: 4px 0; border-radius: 999px; border: 1px solid var(--hv-neutral-700);
	background: rgba(22,24,38,.62); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
	color: var(--hv-neutral-400); opacity: 0.6; transition: opacity .2s ease, color .2s ease;
}

.hub-viewer-foot { position: absolute; left: 0; right: 0; bottom: 0; padding: 18px 20px; display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; pointer-events: none; z-index: 2; }
.hub-viewer-caption { display: flex; flex-direction: column; gap: 5px; min-width: 0; transition: opacity .3s ease, transform .3s ease; }
.hub-viewer-caption-stop { font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--hv-accent-300); }
.hub-viewer-caption-name { font-family: var(--wt-font-display); font-weight: 500; font-size: clamp(17px, 2.4vw, 24px); line-height: 1.2; }
.hub-viewer-caption-note { font-size: 12.5px; line-height: 1.45; color: var(--hv-neutral-200); max-width: 44ch; }
.hub-viewer-arrows { display: flex; gap: 8px; pointer-events: auto; flex: 0 0 auto; }

.hub-viewer-edge-cue {
	position: absolute; bottom: 96px; left: 50%; transform: translateX(-50%) translateY(8px);
	padding: 7px 15px; border-radius: 999px; pointer-events: none; z-index: 3;
	border: 1px solid var(--hv-accent-600); background: rgba(22,24,38,.7); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
	font-family: var(--wt-font-display); font-size: 11.5px; letter-spacing: 0.05em; white-space: nowrap; opacity: 0;
	transition: opacity .25s ease, transform .25s ease;
}
.hub-viewer-hint {
	position: absolute; top: 16px; left: 50%; transform: translateX(-50%);
	padding: 7px 15px; border-radius: 999px; pointer-events: none; z-index: 3;
	border: 1px solid var(--hv-accent-600); background: rgba(22,24,38,.66); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
	font-family: var(--wt-font-display); font-size: 11.5px; letter-spacing: 0.06em; color: var(--hv-neutral-100);
	transition: opacity .5s ease;
}

.hub-viewer-foot-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.hub-viewer-dots { display: flex; gap: 8px; flex-wrap: wrap; }
.hub-viewer-dot {
	appearance: none; height: 4px; width: 22px; padding: 0; border: none; border-radius: 999px;
	background: var(--hv-neutral-700); cursor: pointer; opacity: 0.7;
	transition: width .35s cubic-bezier(.2,.85,.24,1), background .2s ease, opacity .2s ease;
}
.hub-viewer-dot:hover { opacity: 1; }
.hub-viewer-dot.is-active { width: 46px; background: var(--hv-accent); opacity: 1; }
.hub-viewer-hint-static { font-family: var(--wt-font-display); font-size: 11.5px; letter-spacing: 0.04em; color: var(--hv-neutral-400); }

@media (max-width: 640px) {
	.hub-viewer-foot { flex-direction: column; align-items: flex-start; }
	.hub-viewer-arrows { pointer-events: auto; }
	.hub-viewer-hint-static { display: none; }
}

/* 5. 28 August 2026, later same day — full-bleed hero variant of the
   viewer above, used via wantok_banner_hub_viewer_bg()'s '_hero' mode
   (page-content-render.php) instead of the plain boxed .hub-viewer.
   Everything below is scoped under the modifier class that mode adds,
   .hub-viewer--hero, so the boxed, in-flow variant (unused right now,
   kept as reusable infrastructure) is completely untouched.
   Soana: "no rectangle" (27 Aug, said about the earlier iframe-embed
   prototype's identical box) — same fix here: strip the section/inner/
   stage down to a plain absolute inset:0 layer with no padding, no
   border, no radius, no shadow, no capped aspect-ratio, so the photo
   genuinely fills the whole banner edge to edge. */
.page-title-banner-hub-viewer-bg .hub-viewer--hero {
	position: absolute; inset: 0; padding: 0; background: none;
}
.page-title-banner-hub-viewer-bg .hub-viewer--hero .hub-viewer-inner {
	position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; gap: 0;
}
.page-title-banner-hub-viewer-bg .hub-viewer--hero .hub-viewer-stage {
	position: absolute; inset: 0; width: 100%; height: 100%; aspect-ratio: auto;
	max-width: none; border-radius: 0; border: 0; box-shadow: none;
}
/* Soana (28 Aug, said about the same iframe-embed prototype): "remove
   the text, except a small one down the middle about how to navigate
   the 360 view." This component's own kicker/title/lead row and "NN of
   07 · stop name" counter are redundant with the page's own eyebrow/
   title/intro floating on top of this same photo (see
   .page-title-banner--hub-viewer-hero above) — hidden here rather than
   in the shared, always-rendered PHP/JS so the boxed non-hero variant
   (which has no separate page banner to duplicate) keeps showing them. */
.page-title-banner-hub-viewer-bg .hub-viewer--hero .hub-viewer-head,
.page-title-banner-hub-viewer-bg .hub-viewer--hero .hub-viewer-caption-stop,
.page-title-banner-hub-viewer-bg .hub-viewer--hero .hub-viewer-caption-name,
.page-title-banner-hub-viewer-bg .hub-viewer--hero .hub-viewer-caption-note,
.page-title-banner-hub-viewer-bg .hub-viewer--hero .hub-viewer-hint-static {
	display: none;
}
/* The one small piece of on-screen guidance Soana asked to keep — already
   centred by .hub-viewer-hint's own top:16px/left:50% rule, no position
   change needed here. Its opacity is set inline by the shared JS
   (elHint.style.opacity, page-content-render.php) — hardcoded to always
   '1' there now instead of fading after 4.5s/first interaction. */
/* Prev/next arrows + pagination dots need to move from their normal
   in-flow positions (below the now off-screen-absolutely-positioned
   stage) to overlay the bottom of the full-bleed photo instead — the
   empty .hub-viewer-caption div (still rendered, just emptied above)
   stays as the arrows' flex sibling so .hub-viewer-foot's existing
   justify-content:space-between still pushes them to the right, exactly
   as in the boxed variant. */
.page-title-banner-hub-viewer-bg .hub-viewer--hero .hub-viewer-foot-row {
	position: absolute; left: 16px; right: 16px; bottom: 18px; z-index: 5;
	width: auto; max-width: none;
}
@media (max-width: 700px) {
	.page-title-banner-hub-viewer-bg .hub-viewer--hero .hub-viewer-foot-row {
		left: 12px; right: 12px; bottom: 14px;
	}
}
