/**
 * ═══════════════════════════════════════════════════════════════════════
 * TNT Bootstrap Manager — Frontend CSS
 * ───────────────────────────────────────────────────────────────────────
 * SOURCE OF TRUTH for the entire design system: tokens, themes,
 * components, responsive patterns. Sections reference this file.
 *
 * Bootstrap version: 2.25
 * Plugin version:    1.10.0
 *
 * ═══════════════════════════════════════════════════════════════════════
 * ARCHITECTURAL RULE — read this before adding ANYTHING here
 * ═══════════════════════════════════════════════════════════════════════
 *
 *   BOOTSTRAP IS THE PANTRY. SECTIONS ARE THE CHEFS.
 *
 * Bootstrap stocks the shelves with branded INGREDIENTS:
 *     • Design tokens (colors, gradients, fonts, breakpoints)
 *     • Component stamps (.tnt-banner, .tnt-glass-panel, etc.)
 *     • Behavioral engines (toast, prompt, theme toggle)
 *     • Button visual styles (token bundles)
 *     • Documented responsive patterns (see below)
 *
 * Sections cook DISHES — they own:
 *     • Their own structural layout (grid, flex, positioning)
 *     • Their own responsive collapse behavior
 *     • Which bootstrap ingredients to pull in
 *
 * THIS FILE MUST NOT:
 *     ✗ Define structural layout for any section's children
 *       (e.g. NO `.tnt-fb__body { display: grid }` here)
 *     ✗ Force responsive collapse on section-internal elements
 *     ✗ Couple sections together via shared structural rules
 *
 * Component classes here (.tnt-glass-panel, .tnt-banner, etc.) provide
 * VISUAL TREATMENT only. Sections compose them with their own layout.
 *
 * Full architecture explanation: ARCHITECTURE.md (plugin root).
 *
 * ═══════════════════════════════════════════════════════════════════════
 * RESPONSIVE PATTERNS — section authors should follow these practices
 * ═══════════════════════════════════════════════════════════════════════
 *
 * These are documented BEST PRACTICES, not pre-built components.
 * Each section implements the pattern in its own scoped CSS+JS using
 * the bootstrap's tokens (colors, breakpoints, etc.) as ingredients.
 * Documenting the pattern HERE means future sections handle similar
 * situations consistently.
 *
 * ───────────────────────────────────────────────────────────────────────
 * PATTERN: Tag-Cycler  (compresses pill-row → fade-cycler at narrow viewports)
 * ───────────────────────────────────────────────────────────────────────
 *
 * WHEN TO USE:
 *   When a section has a horizontal list of similar pill/badge/tag
 *   items (4+ items typically) that would wrap to multiple lines on
 *   narrow viewports and consume too much vertical space. Examples:
 *     • Superhero section's service tags (8 tags)
 *     • Construction Strip's shipped-features eyebrow (4 pills)
 *     • Future sections: technology badges, certification chips, etc.
 *
 *   Apply this pattern whenever a "label group" or "pill row" would
 *   look cluttered when wrapped on phones / skinny viewports.
 *
 * BEHAVIOR:
 *   At desktop (> --tnt-bp-md): pills wrap normally as a horizontal row.
 *   At narrow (<= --tnt-bp-md): pills compress into a single fade-
 *   cycling slot — one visible at a time, fading through them at ~2.5s
 *   each, with a small pulsing dot signaling "this is cycling."
 *   Hover/focus pauses the rotation for accessibility.
 *
 * IMPLEMENTATION SKELETON:
 *
 *   MARKUP — wrap the existing list/ul in a cycler container:
 *     <div class="{section}__cycler" data-{section}-cycler>
 *       <ul class="{section}__items">
 *         <li>...</li>
 *         <li>...</li>
 *         <li>...</li>
 *       </ul>
 *     </div>
 *
 *   CSS — at narrow viewports, collapse children to absolute-positioned
 *   stage; use [.is-active] class to control visibility:
 *     @media (max-width: 767.98px) {
 *       .{section}__cycler {
 *         position: relative;
 *         height: 2rem;             // matches one pill height
 *         overflow: visible;
 *       }
 *       .{section}__items {
 *         margin: 0; padding: 0;
 *         height: 100%;
 *         display: block;
 *         position: relative;
 *       }
 *       .{section}__items > li {
 *         position: absolute;
 *         top: 0; left: 50%;
 *         transform: translateX(-50%) translateY(4px);
 *         opacity: 0;
 *         pointer-events: none;
 *         transition: opacity 0.5s ease, transform 0.5s ease;
 *         white-space: nowrap;
 *       }
 *       .{section}__items > li.is-active {
 *         opacity: 1;
 *         transform: translateX(-50%) translateY(0);
 *         pointer-events: auto;
 *       }
 *     }
 *
 *   JS — section-local IIFE adds .is-active to one item at a time,
 *   only when matchMedia matches narrow viewport:
 *     (function () {
 *       var cycler = section.querySelector('[data-{section}-cycler]');
 *       if (!cycler) return;
 *       var items = Array.prototype.slice.call(cycler.querySelectorAll('li'));
 *       if (items.length < 2) return;
 *       var TICK_MS = 2500;
 *       var idx = 0, timer = null, paused = false, active = false;
 *
 *       function setActive(i) {
 *         items.forEach(function(li, n) {
 *           li.classList.toggle('is-active', n === i);
 *         });
 *         idx = i;
 *       }
 *       function tick() { if (!paused) setActive((idx+1) % items.length); }
 *       function start() { setActive(0); timer = setInterval(tick, TICK_MS); }
 *       function stop() { if (timer) clearInterval(timer); timer = null;
 *                          items.forEach(function(li){ li.classList.remove('is-active'); }); }
 *
 *       cycler.addEventListener('mouseenter', function(){ paused = true; });
 *       cycler.addEventListener('mouseleave', function(){ paused = false; });
 *       cycler.addEventListener('focusin',    function(){ paused = true; });
 *       cycler.addEventListener('focusout',   function(){ paused = false; });
 *
 *       var bpMd = parseInt(getComputedStyle(document.documentElement)
 *         .getPropertyValue('--tnt-bp-md'), 10) || 768;
 *       var mql = window.matchMedia('(max-width: ' + (bpMd - 1) + 'px)');
 *
 *       function sync(e) {
 *         var matches = e ? e.matches : mql.matches;
 *         if (matches && !active) { active = true; start(); }
 *         else if (!matches && active) { active = false; stop(); }
 *       }
 *       sync();
 *       if (mql.addEventListener) mql.addEventListener('change', sync);
 *       else if (mql.addListener) mql.addListener(sync);
 *     })();
 *
 * REFERENCE IMPLEMENTATIONS in active sections:
 *   • Superhero v27.3 — service tag eyebrow (8 items, .tnt-superhero__tag-cycler)
 *   • Construction Strip v20.4 — shipped pills (4 items, .tnt-fb__shipped-cycler)
 *
 * ───────────────────────────────────────────────────────────────────────
 * PATTERN: Section Eyebrow  (key-phrase row above every section)
 * ───────────────────────────────────────────────────────────────────────
 *
 * SITE-WIDE STANDARD — every content section MUST have an eyebrow
 *   row of key phrases / topic tags placed ABOVE the section's main
 *   container, OUTSIDE the glass panel (or whatever the section's
 *   primary content wrapper is). The eyebrow is the section's "what
 *   this is about" header at-a-glance.
 *
 * INTENT:
 *   • Improves SEO — semantic keywords near the top of each section
 *   • Improves scannability — visitors get a 1-second read of what's coming
 *   • Reinforces brand visual rhythm — every section opens the same way
 *
 * WHAT BELONGS in an eyebrow:
 *   ✓ Topic tags / service names / technology badges / category labels
 *   ✓ Pill-shaped, accent-colored, 3-8 items typically
 *   ✓ Each item ideally clickable (links to relevant deeper page)
 *
 * WHAT DOES NOT belong:
 *   ✗ Single sentences (use a regular caption or the eyebrow ribbon)
 *   ✗ Location details, breadcrumbs, dates — those go elsewhere
 *   ✗ Status indicators ("Live", "Beta") — those use .tnt-badge-live
 *
 * STRUCTURE:
 *   <section id="tnt-{slug}" class="tnt-{slug}">
 *     <!-- 1. EYEBROW (outside the main container, above) -->
 *     <div class="tnt-{slug}__eyebrow-cycler" data-{slug}-eyebrow-cycler>
 *       <ul class="tnt-{slug}__eyebrow-row" role="list">
 *         <li><a href="/topic-1" class="tnt-{slug}__eyebrow-pill">Topic 1</a></li>
 *         <li><a href="/topic-2" class="tnt-{slug}__eyebrow-pill">Topic 2</a></li>
 *         ...
 *       </ul>
 *     </div>
 *
 *     <!-- 2. MAIN CONTENT (glass panel or other primary container) -->
 *     <div class="tnt-{slug}__inner">
 *       <div class="tnt-glass-panel">
 *         ... section content ...
 *       </div>
 *     </div>
 *   </section>
 *
 * RESPONSIVE COLLAPSE:
 *   Eyebrows MUST follow the Tag-Cycler pattern (above) at narrow
 *   viewports — pills compress to a single fade-cycler at ≤768px so
 *   they don't wrap and consume vertical space. Implementation
 *   skeleton same as the Tag-Cycler section above; the wrapper just
 *   uses an "eyebrow" name instead of "tag".
 *
 * STYLING NOTES:
 *   • Eyebrow pills use the .tnt-superhero__tag visual treatment as
 *     the canonical look (small, accent-colored, hover-lifted) but
 *     each section may scope its own variant if a different visual
 *     fits better. Token `--tnt-tag-*` is the source of truth.
 *   • Eyebrow row sits above with breathing room (margin-bottom on
 *     the cycler ~1rem-1.5rem) so it visually announces the section.
 *   • Per the architectural rule, eyebrow LAYOUT is owned by each
 *     section. The pattern is documented; the styling is shared via
 *     bootstrap tokens.
 *
 * REFERENCE IMPLEMENTATIONS:
 *   • Superhero v27.3 — service tags ARE the eyebrow (8 items)
 *   • Construction Strip v20.4 — shipped-pills serve as eyebrow (4 items)
 *
 * ───────────────────────────────────────────────────────────────────────
 * PATTERN: CTA Click Press  (visual feedback on button click)
 * ───────────────────────────────────────────────────────────────────────
 *
 * GLOBAL BEHAVIOR — every TNT button using a button-tier token bundle
 * (.tnt-superhero__cta, .tnt-fb__btn, .tnt-info-cta, .tnt-prompt__btn,
 * .tnt-superhero__tag, .tnt-superhero__theme-toggle, or any element
 * with .tnt-press class) automatically gets a click-press animation
 * via the global :active rules in this file (see "BUTTON PRESS
 * ANIMATION" block below).
 *
 * BEHAVIOR:
 *   On :active (mousedown / touch-active):
 *     • Button presses down 1px + scales to 98.5%
 *     • A quick radial ring pulses outward via box-shadow
 *   On release:
 *     • Bouncy ease-out (cubic-bezier overshoot) returns to rest
 *
 * NEW SECTIONS NEED NO ACTION — using a TNT button-tier class auto-
 * inherits the press animation. To opt in custom button-like elements,
 * add the .tnt-press class to them.
 *
 * EXPOSED TOKENS:
 *   --tnt-btn-press-translate, --tnt-btn-press-scale,
 *   --tnt-btn-press-duration, --tnt-btn-press-release,
 *   --tnt-btn-press-ring-color
 *
 * ───────────────────────────────────────────────────────────────────────
 * PATTERN: Confirm-Toast Button Text Flash  (per-button press feedback)
 * ───────────────────────────────────────────────────────────────────────
 *
 * BEHAVIOR (engine-driven, no section authoring required):
 *   When a confirm-toast trigger arms (toast appears + paging starts):
 *     1. Engine reads the trigger's `data-tnt-confirm-prompt` attribute
 *        (e.g. "Click to start.", "Click to flip.") — falls back to a
 *        generic "Click to confirm" if not set
 *     2. Engine swaps the trigger button's visible text to that prompt
 *     3. Adds a brief eye-catching pulse animation
 *     4. After ~1 second, swaps back to the original text
 *     5. Toast continues running its own state machine independently
 *
 * INTENT:
 *   Reinforces the "this requires confirmation" cue at TWO points:
 *     • The toast (above the button, explanatory)
 *     • The button itself (immediate, where the eye is)
 *   The 1-second flash draws attention without "stealing" the button's
 *   identity for the full armed window.
 *
 * SECTION AUTHORING:
 *   This is engine-driven; sections need to do nothing. Just provide
 *   data-tnt-confirm-prompt as before. The flash uses that text.
 *
 * ───────────────────────────────────────────────────────────────────────
 * PATTERN: Section Stage System  (multi-stage sections with switching)
 * ───────────────────────────────────────────────────────────────────────
 *
 * BOOTSTRAP-OWNED ENGINE — sections compose stages, the engine handles
 * visibility + transitions. Lets a section host MULTIPLE STAGES as
 * stacked layers, with a clean way to switch between them.
 *
 * VOCABULARY:
 *   SECTION  — the whole content section (e.g. "Superhero")
 *   STAGE    — full-width transparent wrapper inside the section.
 *              Multiple stages can exist; one or more visible at a
 *              time. The Stage System manages their visibility.
 *   LAYER    — z-index positioning concept. Each stage occupies
 *              its own layer in the section's z-stack.
 *   PANEL    — a content frame INSIDE a stage. Has its own bg
 *              styling (e.g. .tnt-glass-panel) or no background.
 *
 * TWO STAGE MODES:
 *   • EXCLUSIVE — only ONE visible per stack at a time. Used for
 *     content stages (default view, artwork view, demo view, etc).
 *   • INDEPENDENT — toggles on/off without affecting siblings. Used
 *     for layers like animated backgrounds that should remain
 *     visible across content swaps but be hideable on demand.
 *
 * USE CASES:
 *   • A section with a "default" content stage and an alternate
 *     "artwork view" stage (Superhero's Show Artwork pattern)
 *   • A section with an animated bg layer that can be hidden to
 *     reveal a deeper site-level background
 *   • Future: tabbed content sections, demo/details toggles, etc.
 *
 * STACK LAYOUT:
 *   The .tnt-stage-stack container uses CSS GRID stacking — every
 *   stage occupies the same grid cell. The cell auto-sizes to the
 *   tallest stage's natural content. This means responsive content
 *   never gets cut off; the stack grows to fit.
 *
 * MARKUP CONTRACT:
 *   <div class="tnt-{slug}__stack tnt-stage-stack" id="optional-id">
 *     <!-- Independent (always-on by default) bg layer -->
 *     <div class="tnt-stage" data-tnt-stage="bg"
 *          data-tnt-stage-mode="independent">
 *       ...animated background SVG/canvas/css...
 *     </div>
 *
 *     <!-- Exclusive content stages -->
 *     <div class="tnt-stage" data-tnt-stage="default" data-tnt-stage-default>
 *       ...primary content + a switcher to "artwork"...
 *       <button data-tnt-stage-switch="artwork">Show Artwork</button>
 *     </div>
 *     <div class="tnt-stage" data-tnt-stage="artwork">
 *       ...artwork view content...
 *       <button data-tnt-stage-switch="default">Back</button>
 *       <button data-tnt-stage-toggle="bg">Hide section bg</button>
 *     </div>
 *   </div>
 *
 * ATTRIBUTES:
 *   data-tnt-stage="name"           required, stage identifier
 *   data-tnt-stage-mode="..."       "exclusive" (default) | "independent"
 *   data-tnt-stage-default          marks the initial visible exclusive
 *   data-tnt-stage-hidden           independent stage starts hidden
 *
 * BUTTONS:
 *   data-tnt-stage-switch="name"    show this exclusive (hides siblings)
 *   data-tnt-stage-toggle="name"    flip independent on/off
 *   data-tnt-stage-stack-id="id"    optional — target a stack by its
 *                                   id attribute when the button is
 *                                   in a different stack. Defaults to
 *                                   closest .tnt-stage-stack ancestor.
 *
 * TRANSITION:
 *   Default = FADE (300ms). Override per-stack via CSS variable:
 *     .my-stack { --tnt-stage-transition-duration: 500ms; }
 *
 *   Future transitions can be added to the bootstrap library
 *   (slide, instant-swap, etc.) — just CSS variants on the stack.
 *
 * EVENTS:
 *   `tnt:stage-change` fires on the stack with detail:
 *     { stack, stage, mode, action: 'show'|'hide', ts }
 *
 * SECTION AUTHORING:
 *   Wrap your stages in .tnt-stage-stack. Each stage gets the .tnt-stage
 *   class + a data-tnt-stage name. The engine handles all visibility,
 *   click delegation, and transitions. Sections never manipulate the
 *   --visible/--hidden classes manually.
 *
 * REFERENCE: see "SECTION STAGE ENGINE" IIFE near the bottom of
 *   tnt-bootstrap.js for the implementation.
 *
 * ───────────────────────────────────────────────────────────────────────
 * PATTERN: Caption Fader  (cycle multiple lines through one slot)
 * ───────────────────────────────────────────────────────────────────────
 *
 * BOOTSTRAP-OWNED — for tight spaces where a section needs to deliver
 * multiple short captions in a single text slot. Lines stack on top
 * of each other; one is faded in at a time, cycling.
 *
 * WHEN TO USE:
 *   • Compact header bands where vertical space is at a premium
 *   • Status pills that rotate through descriptors
 *   • Hero straplines with multiple value propositions
 *   • Any place where you'd otherwise show 2-4 short paragraphs
 *     stacked vertically — instead show one at a time, in place
 *
 * MARKUP:
 *   <div class="tnt-caption-fader" data-tnt-caption-fader>
 *     <span class="tnt-caption-fader__line">First caption.</span>
 *     <span class="tnt-caption-fader__line">Second caption.</span>
 *     <span class="tnt-caption-fader__line">Third caption.</span>
 *   </div>
 *
 * ATTRIBUTES:
 *   data-tnt-caption-fader-hold="2200"  per-line hold ms (default 2200)
 *   data-tnt-caption-fader-fade="280"   fade ms (default 280)
 *   data-tnt-caption-fader-pause-on-hover="0"  disable hover pause
 *
 * BEHAVIOR:
 *   Pauses on hover/focus by default. Pauses when tab is hidden.
 *   Sizes to the natural height of whichever line is shown — when
 *   lines have different heights, the slot adjusts smoothly.
 *
 * REFERENCE: see "CAPTION FADER ENGINE" IIFE in tnt-bootstrap.js.
 *
 * ───────────────────────────────────────────────────────────────────────
 * PATTERN: Info-Toast  (one-shot button hint on viewport entry)
 * ───────────────────────────────────────────────────────────────────────
 *
 * COMPLEMENT to the confirm-toast. Same visual treatment (pill, slide
 * paging, dots) but a different lifecycle — single-click button gets
 * an explanatory toast that appears once when the button enters the
 * viewport, pages through its captions, then auto-dismisses. No
 * second-click, no text flash, no commit machinery.
 *
 * WHEN TO USE:
 *   For low-stakes buttons that benefit from explanation but don't
 *   need a confirm-gate. Examples:
 *     • A reset button — "Clears all filters" then dismisses
 *     • A toggle that shows/hides supplementary content
 *     • A minor utility ("Copy link", "Print this page")
 *     • A sidebar action where context isn't obvious from the label
 *
 * WHEN NOT TO USE:
 *   • Actions with consequences (navigation away, state change,
 *     destructive actions) — use confirm-toast (double-click) instead.
 *   • Buttons where the label is fully self-explanatory.
 *   • Buttons that would be cluttered by a hovering toast.
 *
 * ISOLATED FROM CONFIRM-TOAST:
 *   A button uses ONE OR THE OTHER, never both. Both systems on the
 *   same trigger would create competing toasts. The two patterns are
 *   intentionally independent — different markup attributes,
 *   different engines, different lifecycles.
 *
 * MARKUP CONTRACT (parallel to confirm-toast):
 *   <button data-tnt-info-toast-id="{section}-{role}"
 *           data-tnt-info-toast-sm="Quick info."
 *           data-tnt-info-toast-md="A bit more info | here."
 *           data-tnt-info-toast-lg="Full info | with paging | as needed.">
 *     Click me
 *   </button>
 *
 *   Responsive variants follow the same -sm / -md / -lg / fallback
 *   hierarchy as confirm-toast. Pages separated by ` | ` (pipe).
 *
 * BEHAVIOR:
 *   1. IntersectionObserver watches the trigger
 *   2. When ≥40% enters the viewport, a 1-SECOND DEBOUNCE starts
 *      (v2.21+). The element must remain in viewport for the full
 *      second before the toast fires — prevents drive-by triggers
 *      when scrolling fast past the section.
 *   3. Toast pages through captions (~1.1s hold each, ~0.22s fade)
 *   4. Holds last page for ~1.5s then auto-fades out
 *   5. Trigger fires once per page-view (reload to see again)
 *
 * SECTION AUTHORING:
 *   Sections add the data attributes; the engine handles everything
 *   else. No section-side JS needed unless you want analytics on
 *   the toast appearance event.
 *
 * REFERENCE: see "INFO-TOAST ENGINE" IIFE at the bottom of
 *   tnt-bootstrap.js for the implementation.
 *
 * ───────────────────────────────────────────────────────────────────────
 * PATTERN: Exit-Toast  (last-second pitch as element scrolls toward top)
 * ───────────────────────────────────────────────────────────────────────
 *
 * COMPLEMENT to info-toast. Where info-toast greets the user as they
 * scroll INTO an element, exit-toast pitches them as they scroll OUT.
 * Same visual treatment as info-toast/confirm-toast paging (pill, slide
 * paging, caption fader), but:
 *   • Position: BELOW the trigger (since trigger is near top of viewport,
 *     "below" is the visible area where users will see it)
 *   • Trigger condition: trigger's top edge enters the "danger zone" —
 *     the band ~120px below the very top of viewport. This buffer
 *     accounts for site chrome (40px static bar + 40px optional
 *     secondary menu + 40px breathing room).
 *   • Fires once per page-view (Set-tracked). Never re-fires.
 *
 * USE CASES:
 *   • Promote a button the user is about to scroll past
 *   • Last-second clickbait for important actions
 *   • Draw attention to an element that hasn't been clicked yet
 *
 * MARKUP CONTRACT (parallel to info-toast):
 *   <button data-tnt-exit-toast-id="superhero-location"
 *           data-tnt-exit-toast-sm="One last look."
 *           data-tnt-exit-toast-md="See us on Google | before you scroll."
 *           data-tnt-exit-toast-lg="Wait — | see TNT on Google | before you scroll past.">
 *     Click me
 *   </button>
 *
 * ───────────────────────────────────────────────────────────────────────
 * THREE TOAST TYPES — composable on a single button
 * ───────────────────────────────────────────────────────────────────────
 *
 * The same button can carry ALL THREE attribute sets and each engine
 * fires independently:
 *
 *   data-tnt-info-toast-id  → info-toast on viewport ENTRY (after 1s delay)
 *   data-tnt-exit-toast-id  → exit-toast on viewport EXIT-approach
 *   data-tnt-confirm-id     → confirm-toast on user CLICK (double-click gate)
 *
 * They share the same DOM class (.tnt-confirm-toast) for visual identity
 * but maintain independent state:
 *   • info fires once at viewport entry  (1s delay debounce)
 *   • exit fires once at top-approach     (no debounce, instant)
 *   • confirm fires anytime user clicks   (always available)
 *
 * If user clicks the trigger while info or exit toast is showing,
 * the engine dismisses the showing toast and the confirm engine arms.
 * Each engine respects "fired once per page" within its own scope.
 *
 * REFERENCE: see "EXIT-TOAST ENGINE" IIFE at the bottom of
 *   tnt-bootstrap.js for the implementation.
 *
 * ───────────────────────────────────────────────────────────────────────
 * PATTERN: Pre-Action Gate  (in-button text flash for "not yet" gating)
 * ───────────────────────────────────────────────────────────────────────
 *
 * Lightweight gating without modals or toasts. A button declares a
 * gate id + flash messages; section JS sets the active state when
 * the gate should hold; clicks during the active state flash the
 * gate message inside the button text and BLOCK the real action.
 * When the gate condition clears, the section removes the active
 * state and the button works normally.
 *
 * USE CASES:
 *   • Vote buttons that should fire only after a video plays
 *   • Submit buttons disabled while a prerequisite is unmet
 *   • Coupon-redeem buttons that need a min cart value
 *
 * MARKUP CONTRACT:
 *   <button data-tnt-gate-id="hero-act1-vote-pre-play"
 *           data-tnt-gate-sm="Watch first."
 *           data-tnt-gate-md="Watch the video first."
 *           data-tnt-gate-lg="Watch the video first to vote.">
 *     Loved it
 *   </button>
 *
 * ATTRIBUTES:
 *   data-tnt-gate-id          required, unique gate identifier
 *   data-tnt-gate-sm/md/lg    flash messages by viewport
 *   data-tnt-gate-default     "active" → start gated on init
 *   data-tnt-gate-active      runtime: section sets/removes this
 *
 * STATES:
 *   [data-tnt-gate-active] — gate is currently holding (clicks blocked)
 *   .tnt-gate-flashing     — toast text is being shown right now
 *
 * COMPOSABILITY: a button can carry data-tnt-gate-id PLUS info-toast
 * PLUS exit-toast PLUS confirm-id all together. The gate engine
 * intercepts in CAPTURE phase BEFORE other engines, but only when
 * [data-tnt-gate-active] is set. Once the section removes that
 * attribute, all the other engines work normally.
 *
 * REFERENCE: see "PRE-ACTION GATE ENGINE" IIFE in tnt-bootstrap.js.
 *
 * ───────────────────────────────────────────────────────────────────────
 *
 * ═══════════════════════════════════════════════════════════════════════
 * SECTION IDENTITY CONTRACT  (Tier 4 — Discovery & Override System)
 * ═══════════════════════════════════════════════════════════════════════
 *
 * Every TNT section MUST follow these rules so the plugin can identify,
 * snapshot, and override its content.
 *
 * 1. SECTION ROOT ELEMENT carries id="tnt-{slug}"
 *    Example: <section id="tnt-superhero" ...>
 *    The slug is the canonical key used throughout the plugin.
 *    Slugs MUST be lowercase, hyphenated, no spaces.
 *
 *    ──────────────────────────────────────────────────────────────
 *    SYSTEM BREAKPOINTS  (Bootstrap v2.14+ — :root CSS variables)
 *    ──────────────────────────────────────────────────────────────
 *    Sections SHOULD use these canonical breakpoints in @media
 *    queries instead of inventing ad-hoc widths. Industry-standard
 *    tier matching Tailwind/Bootstrap framework norms:
 *
 *      --tnt-bp-sm   640px   phones (portrait), narrow browsers
 *      --tnt-bp-md   768px   tablets (portrait)
 *      --tnt-bp-lg   1024px  tablets (landscape), small laptops
 *      --tnt-bp-xl   1280px  desktops
 *      --tnt-bp-2xl  1536px  large displays
 *
 *    Usage in section CSS:
 *      @media (max-width: 768px) { ... }
 *    OR with calc():
 *      @media (max-width: calc(var(--tnt-bp-md) - 1px)) { ... }
 *
 *    The toast engine's responsive variants (-sm/-md/-lg attributes)
 *    use the same breakpoints — viewport ≤ --tnt-bp-sm picks -sm,
 *    viewport ≤ --tnt-bp-md picks -md, else picks -lg.
 *
 * 2. CONFIRM-TOAST TRIGGERS carry data-tnt-confirm-id="{slug}-{role}"
 *    Example: <button data-tnt-confirm-id="superhero-primary"
 *                     data-tnt-confirm="Opens the | contact form.">
 *    The id format MUST start with the section slug + hyphen + role
 *    (e.g. -primary, -secondary, -theme, -reveal). Plugin uses this
 *    to attach overrides per-button.
 *
 *    ──────────────────────────────────────────────────────────────
 *    THE FOUR-STATE TOAST MACHINE  (Bootstrap engine v2.13+)
 *    ──────────────────────────────────────────────────────────────
 *    Every confirm-toast moves through up to four distinct states.
 *    Each has one role; clicks always advance forward.
 *
 *      ┌─────────┐  paging done   ┌─────────┐  click   ┌────────┐
 *      │ Paging  │ ─────────────> │ Waiting │ ───────> │ Result │ → navigate
 *      └─────────┘                └─────────┘          └────────┘
 *           │                          │                    ▲
 *           │ click during paging      │ 3s timeout         │
 *           │  (skips Waiting)         │  (disarm/close)    │
 *           └─────────────────────────────────────────────> │
 *
 *    Click on toast BODY in any state → Eyebrow ribbon (corner pill,
 *      doesn't change state, auto-fades after ~1.4s).
 *    Click outside trigger or toast → disarm + close.
 *
 *      State 1 — PAGING TOAST: Cycles content pages. Author content
 *                via -sm/-md/-lg attrs. Dots above show progress.
 *      State 2 — WAITING TOAST: Final "Click to confirm." page.
 *                Holds 3s. Rim countdown traces. Author override
 *                via data-tnt-confirm-prompt.
 *      State 3 — RESULT TOAST: Brief inverted commit flash.
 *                Author content via data-tnt-confirm-commit. Empty
 *                = no flash, immediate navigate.
 *      State 4 — EYEBROW RIBBON: Corner pill ("Click the button,
 *                not me.") if user clicks the toast itself. Engine
 *                handles entirely; no section authoring.
 *
 *    Confident users click trigger twice fast: arm → commit. Skips
 *    the Waiting state entirely. Cautious users wait through paging
 *    and Waiting before clicking again. Both paths land at Result.
 *
 *    ──────────────────────────────────────────────────────────────
 *    FULL TOAST ATTRIBUTE REFERENCE  (Bootstrap v2.13+ engine)
 *    ──────────────────────────────────────────────────────────────
 *    The toast engine is the SOLE OWNER of how toasts behave. Each
 *    section is the SOLE OWNER of what each toast says. Sections
 *    decorate trigger elements with data-attributes; the engine
 *    reads them and runs.
 *
 *    REQUIRED on every confirm-toast trigger:
 *      data-tnt-confirm-id="{slug}-{role}"
 *           Unique button id. Used by override system + analytics.
 *
 *    MESSAGE — provide ONE of these forms (engine picks variant):
 *      data-tnt-confirm-sm="..."       used when viewport <= 480px
 *      data-tnt-confirm-md="..."       used when viewport <= 900px
 *      data-tnt-confirm-lg="..."       used otherwise
 *      data-tnt-confirm="..."          fallback when no -sm/-md/-lg
 *
 *      VARIANT FALLBACK HIERARCHY (most specific wins):
 *        On a small screen: -sm → -md → -lg → -confirm
 *        On a medium screen: -md → -lg → -confirm
 *        On a large screen: -lg → -confirm
 *
 *      Each value is one of:
 *        Pipe shorthand:  "Opens the | contact form."
 *        JSON array:      '["Opens", "the form"]'
 *        Plain string:    "Save"     (single page, no animation)
 *
 *    PER-BUTTON UX TEXT (optional, with sensible defaults):
 *      data-tnt-confirm-prompt="Click to confirm."
 *           Final page shown during the 3-second armed window.
 *           Default: "Click to confirm."  (engine's universal default)
 *
 *      data-tnt-confirm-commit="Sending you to | the form…"
 *           "Result Toast" text shown briefly inside the same toast
 *           right after the user confirms (before navigation).
 *           Pipe-split supported but typically a single short page.
 *           Default: empty — no flash, just close & navigate.
 *
 *    PER-BUTTON TIMING OVERRIDES (optional, override Bootstrap defaults):
 *      data-tnt-confirm-page-hold="900"   ms each non-final page holds
 *      data-tnt-confirm-page-fade="240"   ms fade between pages
 *      data-tnt-confirm-duration="3000"   ms armed-window length
 *      data-tnt-confirm-no-paging         skip paging entirely
 *
 *    SECTION-LEVEL EVENT HOOKS (optional):
 *      data-tnt-event="superhero_cta_primary"   analytics event name
 *
 *      tnt:before-arm  (cancelable; fires on the trigger BEFORE
 *                       the engine arms it)
 *           Section listens; call preventDefault() to abort arming.
 *           Useful for empty-input gates, validation, or branching
 *           prompts. The section may re-trigger via .click() after
 *           resolving the branch (use a flag to bypass on re-entry).
 *
 *      tnt:confirmed   (fires on the trigger AFTER the user
 *                       completes the four-state flow)
 *           Section listens to perform the actual action (submit
 *           form, toggle state, etc.). Calling preventDefault()
 *           suppresses the engine's default href navigation.
 *
 *    ──────────────────────────────────────────────────────────────
 *    AUTHORING GUIDANCE — word budgets per viewport variant
 *    ──────────────────────────────────────────────────────────────
 *    Toast width is constrained by --tnt-confirm-toast-max-width
 *    (320px default). Each chunk MUST fit on one line within that
 *    width without scrolling, marquee, or truncation. The author
 *    targets these budgets when splitting:
 *
 *      -sm  (≤480px viewport):  2-3 words/page, 1-3 pages total
 *      -md  (≤900px viewport):  3-4 words/page, 2-3 pages total
 *      -lg  (>900px viewport):  4-5 words/page, 2-4 pages total
 *
 *    Sentences fitting under the budget on ONE line should NOT be
 *    split — keep them as a single chunk for snap clarity. Only
 *    split when the message genuinely benefits from rhythm.
 *
 *    The PROMPT page is a separate page after content; don't
 *    include it in the page count. Same for COMMIT — separate
 *    state, not a paging chunk.
 *
 *    The last content chunk should land on the most consequential
 *    word ("free consultation", not "to start a"). Build → reveal
 *    pattern. Periods at end of final chunk; commas mid-flow.
 *
 *    Prompt text — small CTA matching action's voice:
 *      "Click to confirm.", "Tap again.", "Click to send.",
 *      "Click to switch.", "Click to open."
 *
 *    Commit text — 2-5 words, past-progressive or warm goodbye:
 *      "Sending…", "Switching…", "Opening…", "Returning…"
 *      "Talk soon!", "Thanks!"  (for navigation-away moments)
 *
 *    ──────────────────────────────────────────────────────────────
 *    EXAMPLES — copy these as starting points
 *    ──────────────────────────────────────────────────────────────
 *
 *    Simple (single message, default prompt + no commit flash):
 *      <button data-tnt-confirm-id="faq-call"
 *              data-tnt-confirm="Calls (407) 761-1144.">
 *
 *    Responsive variants (different cadence per viewport):
 *      <a data-tnt-confirm-id="superhero-primary"
 *         data-tnt-confirm-sm="Free consult."
 *         data-tnt-confirm-md="Free consult | starts here."
 *         data-tnt-confirm-lg="Opens our contact form | for a free consultation."
 *         data-tnt-confirm-prompt="Click to start."
 *         data-tnt-confirm-commit="Taking you to the form…">
 *
 *    Theme toggle (state changes, no navigation, custom commit):
 *      <button data-tnt-confirm-id="superhero-theme"
 *              data-tnt-confirm-sm="Flip theme."
 *              data-tnt-confirm-md="Flip the site theme."
 *              data-tnt-confirm-lg="Switches the whole site | between light and dark."
 *              data-tnt-confirm-prompt="Click to flip."
 *              data-tnt-confirm-commit="Switching…">
 *
 *    Service tag (link, opens new page, brief commit):
 *      <a href="/services/it-support"
 *         data-tnt-confirm-id="superhero-tag-it-support"
 *         data-tnt-confirm-sm="IT Support."
 *         data-tnt-confirm-md="Opens IT Support."
 *         data-tnt-confirm-lg="Opens our IT Support service page."
 *         data-tnt-confirm-prompt="Click to open."
 *         data-tnt-confirm-commit="Opening…">
 *
 * 3. INFO POPUPS use <template id="tnt-info-{slug}">
 *    Example: <template id="tnt-info-superhero">...</template>
 *    Exactly one template per section. Plugin overrides by replacing
 *    the template's innerHTML at runtime if an override exists.
 *
 *    ──────────────────────────────────────────────────────────────
 *    PROMPT COMPONENT — branching dialog (Bootstrap v2.15+)
 *    ──────────────────────────────────────────────────────────────
 *    For "are you sure?" / "did you forget X?" branching decisions,
 *    sections call window.tntPrompt() — a Promise-based modal API.
 *    Confirm-toast handles known-action confirmation gates; prompt
 *    handles multi-path branching where the section needs the user
 *    to pick.
 *
 *    USAGE FROM SECTION JS:
 *      window.tntPrompt({
 *        title:   "Add a comment first?",
 *        message: "A word or two tunes the next iteration.",
 *        actions: [
 *          { label: "Add comment", primary: true,  result: "add"  },
 *          { label: "Send anyway", primary: false, result: "send" }
 *        ]
 *      }).then(function (choice) {
 *        if (choice === "add")  inputEl.focus();
 *        if (choice === "send") submitVote();
 *        // null = dismissed (Esc / scrim click)
 *      });
 *
 *    Section owns: title, message, action labels, what each result
 *    triggers. Bootstrap owns: layout, animations, focus management,
 *    Esc-to-dismiss, scrim-click-to-dismiss.
 *
 * 4. SECTIONS MAY register defaults via the PHP filter
 *    `tnt_bsm/register_section_defaults`. This is the cleanest path:
 *
 *      add_filter( 'tnt_bsm/register_section_defaults', function ( $r ) {
 *          $r['superhero'] = [
 *              'toasts' => [
 *                  'superhero-primary' => 'Opens the | contact form.',
 *              ],
 *              'info_popup' => '<h3>Welcome</h3><p>...</p>',
 *          ];
 *          return $r;
 *      } );
 *
 *    Sections that don't use the filter still work — the plugin's
 *    Conveyor scanner and Passive harvester will pick them up from
 *    the rendered HTML, just less efficiently.
 *
 * 5. CONTENT BELONGS TO THE SECTION. The plugin is the framework + manager
 *    — it gathers, stores, and optionally overrides. Authors edit content
 *    in the section's own markup or via the WP admin (override). The
 *    plugin never silently changes section markup.
 *
 * 6. WHEN A SECTION'S DEFAULT CONTENT CHANGES, the plugin's Drift
 *    Detector compares the new hash vs the override's baseline_hash.
 *    Each override carries its own policy:
 *      - auto_revert:     override is auto-cleared on drift
 *      - flag_for_review: drift queues an admin review entry
 *      - keep_override:   drift is silently ignored
 *
 * ═══════════════════════════════════════════════════════════════════════
 * AUDIENCE: Both — developers maintain the tokens here; admins/users
 *           never touch this file directly (Settings page exposes only
 *           the kill switches).
 *
 * TO THE DEVELOPER:
 *   - This file is the COMPILED OUTPUT. The plugin enqueues it on
 *     wp_enqueue_scripts via TNT_BSM\Frontend\Asset_Loader.
 *   - When editing: bump TNT_BSM_BS_VERSION in the plugin file AND
 *     update the changelog block at the top of class-frontend-output.php.
 *   - Minified twin (tnt-bootstrap.min.css) MUST be regenerated when
 *     this file changes. Settings toggle picks which loads.
 *   - Cache-busting via filemtime() is automatic — no manual version
 *     bumps needed unless you specifically want a hard-cut version.
 *
 *   Component changelog (matches Bootstrap component evolution):
 *     v2.4   Panel tier opaque
 *     v2.5   Eyebrow gradient pill, accent button tier
 *     v2.5.1 Secondary CTA inverted
 *     v2.6   Construction Strip patterns extracted
 *     v2.7   .tnt-banner component
 *     v2.8   .tnt-brand-text, .tnt-beams, .tnt-modal
 *     v2.9   HUD icon variant
 *     v2.10  .tnt-glass-panel
 *     v2.11  .tnt-feature-card
 *     v2.12  Yellow tier + .tnt-confirm-toast + .tnt-info-cta
 *     v2.13  Toast UX upgrade: paging dots, thicker rim, click-on-toast
 *            eyebrow, commit flash, responsive splits, centered text
 *     v2.14  System responsive breakpoints (--tnt-bp-sm/md/lg/xl/2xl)
 *     v2.15  .tnt-prompt component (modal-style branching dialog)
 * ═══════════════════════════════════════════════════════════════════════
 */

/* ═════════════════════════════════════════════════════════════
   :root — ALL palette + component tokens.
   ═════════════════════════════════════════════════════════════ */
:root {
  /* ── LAYOUT ── */
  --tnt-header-offset: 100px;

  /* ═════════════════════════════════════════════
     RESPONSIVE BREAKPOINTS  (Bootstrap v2.14+)
     ─────────────────────────────────────────────
     Canonical viewport-width thresholds used
     across all sections. Industry-standard tiers
     (matches Tailwind/Bootstrap framework norms).
     Sections SHOULD use these — do NOT invent
     ad-hoc breakpoints in section CSS.
     ═════════════════════════════════════════════ */
  --tnt-bp-sm:  640px;   /* phones (portrait), narrow browsers */
  --tnt-bp-md:  768px;   /* tablets (portrait) */
  --tnt-bp-lg:  1024px;  /* tablets (landscape), small laptops */
  --tnt-bp-xl:  1280px;  /* desktops */
  --tnt-bp-2xl: 1536px;  /* large displays */

  /* ═════════════════════════════════════════════
     BRAND PALETTE — PRIMARY (cool blues)
     ═════════════════════════════════════════════ */
  --tnt-primary:         #0084FF;
  --tnt-primary-dark:    #172CE3;
  --tnt-primary-darker:  #0B1D9E;
  --tnt-primary-light:   #4DA8FF;
  --tnt-primary-lighter: #A8D3FF;

  /* ═════════════════════════════════════════════
     BRAND PALETTE — SECONDARY (warm orange→yellow)
     ═════════════════════════════════════════════ */
  --tnt-secondary:         #FFA300;
  --tnt-secondary-light:   #FFD500;
  --tnt-secondary-dark:    #D88400;
  --tnt-secondary-lighter: #FFE766;

  /* ═════════════════════════════════════════════
     SIGNATURE GRADIENTS
     ═════════════════════════════════════════════ */
  --tnt-gradient-primary:   linear-gradient(110deg, var(--tnt-primary-dark), var(--tnt-primary));
  --tnt-gradient-secondary: linear-gradient(110deg, var(--tnt-secondary),    var(--tnt-secondary-light));
  --tnt-gradient-primary-rev:   linear-gradient(110deg, var(--tnt-primary),         var(--tnt-primary-dark));
  --tnt-gradient-secondary-rev: linear-gradient(110deg, var(--tnt-secondary-light), var(--tnt-secondary));
  --tnt-gradient-cta:    var(--tnt-gradient-primary);
  --tnt-gradient-accent: var(--tnt-gradient-secondary);

  /* ═════════════════════════════════════════════
     ALPHA TINTS
     ═════════════════════════════════════════════ */
  --tnt-primary-soft:   color-mix(in srgb, var(--tnt-primary) 8%,  transparent);
  --tnt-primary-medium: color-mix(in srgb, var(--tnt-primary) 16%, transparent);
  --tnt-primary-glow:   color-mix(in srgb, var(--tnt-primary) 30%, transparent);

  --tnt-secondary-soft:   color-mix(in srgb, var(--tnt-secondary) 10%, transparent);
  --tnt-secondary-medium: color-mix(in srgb, var(--tnt-secondary) 20%, transparent);
  --tnt-secondary-glow:   color-mix(in srgb, var(--tnt-secondary) 35%, transparent);

  /* ═════════════════════════════════════════════
     SEMANTIC COLORS
     ═════════════════════════════════════════════ */
  --tnt-success: #10B981;
  --tnt-warning: var(--tnt-secondary);
  --tnt-danger:  #EF4444;
  --tnt-info:    var(--tnt-primary);

  /* ═════════════════════════════════════════════
     THEME — LIGHT MODE (default)
     ═════════════════════════════════════════════ */
  --tnt-bg:            #EBF2FC;
  --tnt-surface:       #FFFFFF;
  --tnt-surface-2:     #DDE7F5;

  --tnt-text:          #0F172A;
  --tnt-text-muted:    #5A6B80;
  --tnt-text-inverse:  #FFFFFF;

  --tnt-border:        rgba(15, 23, 42, 0.08);
  --tnt-border-strong: rgba(15, 23, 42, 0.18);

  --tnt-shadow-sm:     0 2px 8px    rgba(15, 23, 42, 0.05);
  --tnt-shadow-md:     0 8px 30px   -10px rgba(15, 23, 42, 0.15);
  --tnt-shadow-lg:     0 30px 80px  -30px rgba(15, 23, 42, 0.22);

  --tnt-scrim:         rgba(235, 242, 252, 0.8);

  /* ── MOTION ── */
  --tnt-transition-theme: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;

  /* ═════════════════════════════════════════════
     WORDMARK PALETTE — TNT warm identity
     ═════════════════════════════════════════════ */
  --tnt-warm-a:         #FF6B1A;
  --tnt-warm-b:         #E63926;
  --tnt-warm-highlight: #FFB800;
  --tnt-warm-gradient:  linear-gradient(120deg, var(--tnt-warm-a), var(--tnt-warm-b));

  /* ═════════════════════════════════════════════
     ROLE TOKENS — LIGHT MODE (default)
     Identity = BLUE  | Accent = WARM
     ═════════════════════════════════════════════ */
  --tnt-identity-a:         var(--tnt-primary);
  --tnt-identity-b:         var(--tnt-primary-dark);
  --tnt-identity-highlight: var(--tnt-primary-light);
  --tnt-identity-gradient:  var(--tnt-gradient-primary);
  --tnt-identity-soft:      var(--tnt-primary-soft);
  --tnt-identity-medium:    var(--tnt-primary-medium);
  --tnt-identity-glow:      var(--tnt-primary-glow);
  --tnt-identity-glow-lg:   color-mix(in srgb, var(--tnt-primary) 40%, transparent);

  --tnt-accent-a:           var(--tnt-warm-a);
  --tnt-accent-b:           var(--tnt-warm-b);
  --tnt-accent-highlight:   var(--tnt-warm-highlight);
  --tnt-accent-gradient:    var(--tnt-warm-gradient);
  --tnt-accent-soft:        color-mix(in srgb, var(--tnt-warm-a) 10%, transparent);
  --tnt-accent-medium:      color-mix(in srgb, var(--tnt-warm-a) 20%, transparent);
  --tnt-accent-glow:        color-mix(in srgb, var(--tnt-warm-a) 30%, transparent);
  --tnt-accent-glow-lg:     color-mix(in srgb, var(--tnt-warm-a) 38%, transparent);

  /* ═════════════════════════════════════════════
     WARNING / YELLOW TIER (v2.12)
     Third visual lane — neither cool identity nor warm
     accent. Used for informational/tertiary actions like
     "More Info" buttons. Saturated yellow (#FFD500-ish)
     family for prominence; auto-flips on theme.
     ═════════════════════════════════════════════ */
  --tnt-warning-a:           #FFD500;             /* primary yellow */
  --tnt-warning-b:           #F4B400;             /* deeper amber-yellow for gradient */
  --tnt-warning-highlight:   #FFE766;             /* lighter highlight tone */
  --tnt-warning-text:        #4A3500;             /* dark amber-brown for legible
                                                    text-on-yellow surfaces */
  --tnt-warning-gradient:    linear-gradient(120deg, var(--tnt-warning-a), var(--tnt-warning-b));
  --tnt-warning-soft:        color-mix(in srgb, var(--tnt-warning-a) 14%, transparent);
  --tnt-warning-medium:      color-mix(in srgb, var(--tnt-warning-a) 26%, transparent);
  --tnt-warning-glow:        color-mix(in srgb, var(--tnt-warning-a) 36%, transparent);
  --tnt-warning-glow-lg:     color-mix(in srgb, var(--tnt-warning-a) 46%, transparent);

  /* ═════════════════════════════════════════════════════════════
     COMPONENT TOKENS
     ═════════════════════════════════════════════════════════════ */

  /* ─── Panel surface — UNIFIED OPAQUE (v2.4) ───────────────── */
  --tnt-panel-subtle-bg:    var(--tnt-surface);
  --tnt-panel-muted-bg:     var(--tnt-surface);
  --tnt-panel-solid-bg:     var(--tnt-surface);
  --tnt-panel-border:       var(--tnt-border-strong);
  --tnt-panel-shadow:       var(--tnt-shadow-sm);
  --tnt-panel-radius:       0 10px 10px 0;
  --tnt-panel-radius-full:  10px;

  /* ─── Shimmer rail ─────────────────────────────────────────── */
  --tnt-rail-width:              4px;
  --tnt-rail-identity:           linear-gradient(180deg, var(--tnt-identity-a) 0%, var(--tnt-identity-b) 50%, var(--tnt-identity-a) 100%);
  --tnt-rail-accent:             linear-gradient(180deg, var(--tnt-accent-a)   0%, var(--tnt-accent-b)   50%, var(--tnt-accent-a)   100%);
  --tnt-rail-shimmer:            rgba(255, 255, 255, 0.9);
  --tnt-rail-shimmer-duration:   3.5s;

  /* ─── Buttons (v2.5) ───────────────────────────────────────── */
  --tnt-btn-radius:     999px;
  --tnt-btn-transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.25s ease, color 0.2s ease, border-color 0.2s ease;

  /* Press-active animation tokens (v2.16+).
     Sections referencing button token bundles get the press effect
     for free via the global :active rules below this block. The
     tokens are exposed so sections can reference them in their own
     custom button styles if needed. */
  --tnt-btn-press-translate:   1px;       /* downward press distance */
  --tnt-btn-press-scale:       0.985;     /* slight squish */
  --tnt-btn-press-duration:    90ms;      /* press-down speed (snappy) */
  --tnt-btn-press-release:     180ms;     /* release-up speed (bouncy) */
  --tnt-btn-press-ring-color:  color-mix(in srgb, currentColor 35%, transparent);

  /* PRIMARY — identity gradient, white text */
  --tnt-btn-primary-bg:            var(--tnt-identity-gradient);
  --tnt-btn-primary-color:         #FFFFFF;
  --tnt-btn-primary-shadow:        0 6px 20px -6px var(--tnt-identity-glow);
  --tnt-btn-primary-shadow-hover:  0 10px 28px -6px var(--tnt-identity-glow-lg);

  /* ACCENT — accent gradient, white text. Visual MIRROR of primary
     using the opposite role. Auto-flips: warm in light, blue in dark.
     Use for "alternate" CTAs of equal weight to primary. */
  --tnt-btn-accent-bg:             var(--tnt-accent-gradient);
  --tnt-btn-accent-color:          #FFFFFF;
  --tnt-btn-accent-shadow:         0 6px 20px -6px var(--tnt-accent-glow);
  --tnt-btn-accent-shadow-hover:   0 10px 28px -6px var(--tnt-accent-glow-lg);

  /* SECONDARY — INVERTED (v2.5.1)
     Rest = accent-bordered + accent text + lifted shadow.
     Hover = accent-gradient FILL (same color family, just filled).
     Reads as: "outlined at rest → filled on hover" — color story
     stays consistent within the accent role across the transition. */
  --tnt-btn-secondary-bg:            var(--tnt-surface);
  --tnt-btn-secondary-color:         var(--tnt-accent-a);
  --tnt-btn-secondary-border:        var(--tnt-accent-a);
  --tnt-btn-secondary-shadow:        0 6px 20px -6px var(--tnt-accent-glow);
  --tnt-btn-secondary-bg-hover:      var(--tnt-accent-gradient);
  --tnt-btn-secondary-color-hover:   #FFFFFF;
  --tnt-btn-secondary-border-hover:  transparent;
  --tnt-btn-secondary-shadow-hover:  0 10px 28px -6px var(--tnt-accent-glow-lg);

  /* WARNING — yellow tier (v2.12)
     Same outlined→filled pattern as secondary, but in the
     warning role. Used for "More Info" or other tertiary
     informational actions. Outlined yellow at rest gives
     a clear "this is a side option" cue without competing
     with primary or accent buttons.
     Hover fills with yellow gradient; text flips to dark
     amber-brown (--tnt-warning-text) for legibility on
     yellow surface. */
  --tnt-btn-warning-bg:            var(--tnt-surface);
  --tnt-btn-warning-color:         var(--tnt-warning-b);
  --tnt-btn-warning-border:        var(--tnt-warning-b);
  --tnt-btn-warning-shadow:        0 6px 20px -6px var(--tnt-warning-glow);
  --tnt-btn-warning-bg-hover:      var(--tnt-warning-gradient);
  --tnt-btn-warning-color-hover:   var(--tnt-warning-text);
  --tnt-btn-warning-border-hover:  transparent;
  --tnt-btn-warning-shadow-hover:  0 10px 28px -6px var(--tnt-warning-glow-lg);

  /* GHOST — transparent with border only */
  --tnt-btn-ghost-bg:          transparent;
  --tnt-btn-ghost-color:       var(--tnt-text);
  --tnt-btn-ghost-border:      var(--tnt-border-strong);
  --tnt-btn-ghost-color-hover: var(--tnt-accent-a);

  /* ─── Eyebrow (v2.5 — opaque gradient pill) ────────────────
     Solid accent gradient fill, white text, white icon.
     Soft pulsing accent halo (configurable duration).
     Auto-flips per role tokens: warm in light, blue in dark. */
  --tnt-eyebrow-bg:             var(--tnt-accent-gradient);
  --tnt-eyebrow-color:          #FFFFFF;
  --tnt-eyebrow-border:         transparent;
  --tnt-eyebrow-shadow:         0 4px 14px -4px var(--tnt-accent-glow);
  --tnt-eyebrow-shadow-hover:   0 8px 22px -6px var(--tnt-accent-glow-lg);
  --tnt-eyebrow-pulse-duration: 3.2s;

  /* ─── Live status badge (v2.6) ─────────────────────────────
     Small pulsing pill signaling "active / live / in progress".
     Distinct from eyebrow (which introduces headlines); this
     is a status indicator that travels with any live element.
     Same visual language as eyebrow but tighter + a pulsing
     dot instead of a halo. */
  --tnt-badge-live-bg:             var(--tnt-accent-gradient);
  --tnt-badge-live-color:          #FFFFFF;
  --tnt-badge-live-border:         transparent;
  --tnt-badge-live-shadow:         0 3px 10px -3px var(--tnt-accent-glow);
  --tnt-badge-live-dot-color:      #FFFFFF;
  --tnt-badge-live-pulse-duration: 2s;

  /* ─── Status chip — ACTIVE variant (v2.6) ──────────────────
     Outlined accent chip with a pulsing leading dot. Reads as:
     "currently in progress." Use in status/roadmap lists
     alongside the DONE variant for completed items. */
  --tnt-chip-active-bg:             var(--tnt-accent-soft);
  --tnt-chip-active-color:          var(--tnt-accent-a);
  --tnt-chip-active-border:         var(--tnt-accent-medium);
  --tnt-chip-active-dot-color:      var(--tnt-accent-a);
  --tnt-chip-active-pulse-duration: 2.4s;

  /* ─── Status chip — DONE variant (v2.6) ────────────────────
     Filled accent chip with a leading check icon. Reads as:
     "completed / shipped / delivered." */
  --tnt-chip-done-bg:      var(--tnt-accent-gradient);
  --tnt-chip-done-color:   #FFFFFF;
  --tnt-chip-done-border:  transparent;
  --tnt-chip-done-shadow:  0 2px 8px -2px var(--tnt-accent-glow);

  /* ─── Ambient blueprint grid (v2.6) ────────────────────────
     Dotted scaffold pattern for full-width section backgrounds.
     Opacity auto-adjusts per theme (boosted in dark mode).
     Consumed as a background-image recipe + opacity pairing. */
  --tnt-ambient-grid-dot-color:   color-mix(in srgb, var(--tnt-identity-a) 35%, transparent);
  --tnt-ambient-grid-dot-size:    1.5px;
  --tnt-ambient-grid-dot-gap:     18px;
  --tnt-ambient-grid-opacity:     0.55;
  --tnt-ambient-grid-mask:        linear-gradient(to bottom, transparent 0%, #000 12%, #000 88%, transparent 100%);

  /* ─── Animated dot beams (v2.8) ────────────────────────────
     Two ambient beams that sweep across a section — one
     horizontal, one vertical. Each is a gradient of accent
     dots moving on its own loop. Provides motion without
     being busy. Use on full-width section backgrounds where
     passive energy is desired (replaces static grid when
     motion is preferred). */
  --tnt-beam-dot-color:            var(--tnt-accent-a);
  --tnt-beam-dot-size:             2px;
  --tnt-beam-dot-gap:              14px;
  --tnt-beam-thickness:            120px;           /* beam band thickness */
  --tnt-beam-opacity:              0.35;            /* beam peak opacity */
  --tnt-beam-duration-horizontal:  14s;
  --tnt-beam-duration-vertical:    18s;             /* different speed = asynchronous feel */
  /* Beam gradient: accent dots fading in + out across the band */
  --tnt-beam-gradient-h:           linear-gradient(90deg,
                                     transparent 0%,
                                     var(--tnt-beam-dot-color) 50%,
                                     transparent 100%);
  --tnt-beam-gradient-v:           linear-gradient(180deg,
                                     transparent 0%,
                                     var(--tnt-beam-dot-color) 50%,
                                     transparent 100%);

  /* ─── Modal / dialog (v2.8) ────────────────────────────────
     Reusable overlay dialog for confirmations, prompts, small
     input forms. Panel uses accent-gradient border (matches
     toast treatment) so it reads as same visual family. */
  --tnt-modal-scrim:           rgba(10, 20, 40, 0.55);
  --tnt-modal-scrim-blur:      6px;
  --tnt-modal-bg:              var(--tnt-surface);
  --tnt-modal-color:           var(--tnt-text);
  --tnt-modal-border:          var(--tnt-border-strong);
  --tnt-modal-radius:          16px;
  --tnt-modal-shadow:          var(--tnt-shadow-lg);
  --tnt-modal-max-width:       440px;
  --tnt-modal-padding:         clamp(1.25rem, 3vw, 1.75rem);

  /* ─── Identity-framed card (v2.6) ──────────────────────────
     Content card that anchors to the brand identity color:
     tinted background + dashed identity-colored border.
     Use for content blocks needing clear brand association. */
  --tnt-frame-identity-bg:        var(--tnt-identity-soft);
  --tnt-frame-identity-border:    var(--tnt-identity-a);
  --tnt-frame-identity-radius:    14px;
  --tnt-frame-identity-shadow:    var(--tnt-shadow-md);

  /* ─── Banner (v2.7) ────────────────────────────────────────
     Reusable panel-with-rail component. Pure styling; sections
     drop any content inside. Rail color set by variant class.

     .tnt-banner--accent   rail = accent role
     .tnt-banner--identity rail = identity role

     Shimmer animation travels the rail. Right-side corners
     rounded; left side is flat so the rail reads as a bookmark.
   */
  --tnt-banner-bg:               var(--tnt-surface);
  --tnt-banner-border:           var(--tnt-border-strong);
  --tnt-banner-shadow:           var(--tnt-shadow-sm);
  --tnt-banner-radius:           0 10px 10px 0;
  --tnt-banner-padding:          1rem clamp(1.25rem, 3vw, 1.75rem);
  --tnt-banner-rail-width:       4px;
  --tnt-banner-rail-accent:      linear-gradient(180deg, var(--tnt-accent-a)   0%, var(--tnt-accent-b)   50%, var(--tnt-accent-a)   100%);
  --tnt-banner-rail-identity:    linear-gradient(180deg, var(--tnt-identity-a) 0%, var(--tnt-identity-b) 50%, var(--tnt-identity-a) 100%);
  --tnt-banner-shimmer:          rgba(255, 255, 255, 0.9);
  --tnt-banner-shimmer-duration: 3.5s;

  /* ─── Glass panel (v2.10) ──────────────────────────────────
     Tinted-glass content wrapper. Frosted backdrop blur softens
     animated section backgrounds behind content; tint maintains
     theme-aware atmosphere; inner highlight + outer shadow
     create the "lit pane" feel.

     Light mode: frosted white-blue glass with cool inner highlight.
     Dark mode:  smoky navy glass with warm inner highlight.

     Composability: section content (banners, cards, lists, CTAs)
     all sit INSIDE the glass panel. Glass is a frame, not a
     content type. */
  --tnt-glass-bg:               color-mix(in srgb, var(--tnt-surface) 78%, transparent);
  --tnt-glass-border:           color-mix(in srgb, var(--tnt-text) 12%, transparent);
  --tnt-glass-highlight:        color-mix(in srgb, #FFFFFF 50%, transparent);
  --tnt-glass-shadow:           var(--tnt-shadow-lg),
                                inset 0 1px 0 var(--tnt-glass-highlight),
                                inset 0 0 0 1px color-mix(in srgb, #FFFFFF 6%, transparent);
  --tnt-glass-blur:             14px;
  --tnt-glass-saturate:         140%;
  --tnt-glass-radius:           1.5rem;
  --tnt-glass-padding:          clamp(1.5rem, 4vw, 3rem);

  /* ─── Feature card (v2.11) ─────────────────────────────────
     Reusable card frame for grids of hoverable items (feature
     tiles, service cards, catalog entries). Pure frame: bg,
     border, radius, shadow, hover lift + shadow + border-shift,
     plus a signature top-edge rail that scales in from center
     on hover.

     Sections own the internal content layout (icon, title,
     description, etc). The card just provides the frame +
     hover interaction.

     Variant: .tnt-feature-card--accent flips rail color from
     identity gradient to accent gradient. */
  --tnt-feature-card-bg:           var(--tnt-surface);
  --tnt-feature-card-border:       var(--tnt-border);
  --tnt-feature-card-border-hover: var(--tnt-identity-medium);
  --tnt-feature-card-radius:       1rem;
  --tnt-feature-card-shadow:       var(--tnt-shadow-sm);
  --tnt-feature-card-shadow-hover: var(--tnt-shadow-md);
  --tnt-feature-card-padding:      clamp(1.5rem, 3vw, 1.75rem) clamp(1.125rem, 2.5vw, 1.5rem);
  --tnt-feature-card-lift:         -4px;
  --tnt-feature-card-rail-height:  3px;
  --tnt-feature-card-rail-bg:      var(--tnt-identity-gradient);
  --tnt-feature-card-rail-bg-accent: var(--tnt-accent-gradient);
  --tnt-feature-card-transition:   transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
  --tnt-feature-card-rail-transition: transform 0.35s ease;

  /* ─── Confirm-toast (v2.12) ────────────────────────────────
     Pill-shaped, single-line tooltip that appears above its
     trigger button on first click as a confirmation gate.
     Pill rim border traces the 3s countdown. Long messages page
     through 2-3 words at a time (no marquee/scroll); rim countdown
     starts only after the last page lands. Each button has its own toast slot;
     arming a different button closes the prior toast.

     Usage on any button:
       <button class="tnt-btn-primary"
               data-tnt-confirm="Will open the contact form."
               data-tnt-confirm-id="superhero-primary">
         Get a Free Consultation
       </button>

     Toast DOM is created lazily by the JS controller and
     portaled into <body> so positioning is independent of
     ancestor stacking contexts. Position is recalculated
     on resize + scroll. */
  --tnt-confirm-toast-bg:            var(--tnt-surface);
  --tnt-confirm-toast-color:         var(--tnt-text);
  --tnt-confirm-toast-border:        var(--tnt-border-strong);
  --tnt-confirm-toast-rim:           var(--tnt-accent-a);
  --tnt-confirm-toast-shadow:        0 12px 30px -10px rgba(0, 0, 0, 0.25);
  --tnt-confirm-toast-radius:        999px;
  --tnt-confirm-toast-padding:       0.55rem 1rem;
  --tnt-confirm-toast-min-width:     180px;
  --tnt-confirm-toast-max-width:     320px;
  --tnt-confirm-toast-gap:           0.65rem;        /* gap above trigger */
  --tnt-confirm-toast-duration:      3000ms;         /* must match JS */
  --tnt-confirm-toast-page-fade:     240ms;          /* fade between pages */
  --tnt-confirm-toast-page-hold:     900ms;          /* time each non-final page is fully visible */
  --tnt-confirm-toast-fade-in:       180ms;
  --tnt-confirm-toast-fade-out:      220ms;
  --tnt-confirm-toast-rim-thickness: 3px;            /* thicker rim countdown */
  --tnt-confirm-toast-bp-sm:         var(--tnt-bp-sm);  /* viewport ≤ this → use -sm split */
  --tnt-confirm-toast-bp-md:         var(--tnt-bp-md);  /* viewport ≤ this → use -md split, else -lg */
  --tnt-confirm-toast-commit-flash:  500ms;          /* commit-flash hold time */
  --tnt-confirm-toast-dots-gap:      6px;            /* gap between paging dots */
  --tnt-confirm-toast-dot-size:      6px;

  /* ─── Info CTA + popup (v2.12) ─────────────────────────────
     "More Info" tertiary button (yellow tier) with a label
     that collapses to icon-only after first being seen
     (4s settle), and a popup modal explaining the section.

     - Button base: var(--tnt-btn-warning-*) tokens (above)
     - Icon-only width transition tuning lives here
     - Popup uses .tnt-modal as base + yellow border treatment

     Section content is provided per-section via:
       <button data-tnt-info="superhero">More Info</button>
       <template id="tnt-info-superhero">
         <h3>About this section</h3>
         <p>...</p>
       </template> */
  --tnt-info-cta-radius:               999px;
  --tnt-info-cta-padding:              0.55rem 1rem;
  --tnt-info-cta-padding-collapsed:    0.55rem;
  --tnt-info-cta-collapse-delay:       4000ms;       /* must match JS */
  --tnt-info-cta-label-transition:     max-width 0.4s ease, margin 0.4s ease, opacity 0.3s ease;
  --tnt-info-popup-border:             var(--tnt-warning-b);
  --tnt-info-popup-accent:             var(--tnt-warning-a);

  /* ─── Feature icon (v2.7) ──────────────────────────────────
     Oversized section-header icon. Distinct from inline icons
     (inline icons inherit currentColor and sit next to text).
     Feature icons are a focal element with their own container,
     size, and optional glow. Use for section intro / badge stacks.
     HUD variant (v2.9): adds a pulsing accent halo + drop-shadow
     filter for frameless luminous icons. Apply via class
     .tnt-icon-feature--hud (consumer-defined) or just reference
     the halo tokens directly. */
  --tnt-icon-feature-size:            72px;
  --tnt-icon-feature-stroke:          2;
  --tnt-icon-feature-color:           var(--tnt-accent-a);
  --tnt-icon-feature-bg:              var(--tnt-accent-soft);
  --tnt-icon-feature-border:          var(--tnt-accent-medium);
  --tnt-icon-feature-radius:          20px;
  --tnt-icon-feature-shadow:          0 8px 24px -8px var(--tnt-accent-glow);
  --tnt-icon-feature-halo-color:      var(--tnt-accent-glow);
  --tnt-icon-feature-halo-size:       140%;
  --tnt-icon-feature-halo-duration:   3.6s;
  --tnt-icon-feature-glow:            drop-shadow(0 0 8px var(--tnt-accent-glow)) drop-shadow(0 2px 4px var(--tnt-accent-medium));

  /* ─── Tag pills ────────────────────────────────────────────── */
  --tnt-tag-bg:           var(--tnt-surface);
  --tnt-tag-color:        var(--tnt-text-muted);
  --tnt-tag-border:       var(--tnt-border);
  --tnt-tag-bg-hover:     var(--tnt-accent-soft);
  --tnt-tag-color-hover:  var(--tnt-accent-a);
  --tnt-tag-border-hover: var(--tnt-accent-a);

  /* ═════════════════════════════════════════════
     LEGACY ALIASES — backward compat
     ═════════════════════════════════════════════ */
  --tnt-brand-primary:          var(--tnt-primary);
  --tnt-brand-secondary:        var(--tnt-primary-dark);
  --tnt-brand-accent:           var(--tnt-secondary);
  --tnt-brand-text:             var(--tnt-text);
  --tnt-brand-primary-soft:     var(--tnt-primary-soft);
  --tnt-brand-primary-medium:   var(--tnt-primary-medium);
  --tnt-brand-primary-glow:     var(--tnt-primary-glow);
}

@media (max-width: 767px) {
  :root { --tnt-header-offset: 50px; }
}

/* ═════════════════════════════════════════════════════════════
   TYPOGRAPHY — from Elementor globals (body scope).
   ═════════════════════════════════════════════════════════════ */
body {
  --tnt-font-heading: var(--e-global-typography-primary-font-family,   sans-serif);
  --tnt-font-sub:     var(--e-global-typography-secondary-font-family, sans-serif);
  --tnt-font-body:    var(--e-global-typography-text-font-family,      sans-serif);
  --tnt-font-ui:      var(--e-global-typography-accent-font-family,    sans-serif);
}

/* ═════════════════════════════════════════════════════════════
   THEME — DARK MODE
   Role tokens flip identity↔accent. All component tokens that
   resolve through role/surface/text/border auto-update.
   ═════════════════════════════════════════════════════════════ */
[data-theme="dark"] {
  --tnt-bg:            #0A1628;
  --tnt-surface:       #152238;
  --tnt-surface-2:     #0F1C2E;

  --tnt-text:          #F0F4F8;
  --tnt-text-muted:    #94A3B8;
  --tnt-text-inverse:  #0F172A;

  --tnt-border:        rgba(240, 244, 248, 0.08);
  --tnt-border-strong: rgba(240, 244, 248, 0.18);

  --tnt-shadow-sm:     0 2px 8px    rgba(0, 0, 0, 0.3);
  --tnt-shadow-md:     0 8px 30px   -10px rgba(0, 0, 0, 0.5);
  --tnt-shadow-lg:     0 30px 80px  -30px rgba(0, 0, 0, 0.7);

  --tnt-scrim:         rgba(10, 22, 40, 0.8);

  --tnt-primary-soft:   color-mix(in srgb, var(--tnt-primary) 12%, transparent);
  --tnt-primary-medium: color-mix(in srgb, var(--tnt-primary) 22%, transparent);
  --tnt-primary-glow:   color-mix(in srgb, var(--tnt-primary) 40%, transparent);

  --tnt-secondary-soft:   color-mix(in srgb, var(--tnt-secondary) 14%, transparent);
  --tnt-secondary-medium: color-mix(in srgb, var(--tnt-secondary) 24%, transparent);
  --tnt-secondary-glow:   color-mix(in srgb, var(--tnt-secondary) 40%, transparent);

  /* Role tokens — FLIPPED (Identity = WARM | Accent = BLUE) */
  --tnt-identity-a:         var(--tnt-warm-a);
  --tnt-identity-b:         var(--tnt-warm-b);
  --tnt-identity-highlight: var(--tnt-warm-highlight);
  --tnt-identity-gradient:  var(--tnt-warm-gradient);
  --tnt-identity-soft:      color-mix(in srgb, var(--tnt-warm-a) 14%, transparent);
  --tnt-identity-medium:    color-mix(in srgb, var(--tnt-warm-a) 24%, transparent);
  --tnt-identity-glow:      color-mix(in srgb, var(--tnt-warm-a) 38%, transparent);
  --tnt-identity-glow-lg:   color-mix(in srgb, var(--tnt-warm-a) 46%, transparent);

  --tnt-accent-a:           var(--tnt-primary);
  --tnt-accent-b:           var(--tnt-primary-dark);
  --tnt-accent-highlight:   var(--tnt-primary-light);
  --tnt-accent-gradient:    var(--tnt-gradient-primary);
  --tnt-accent-soft:        var(--tnt-primary-soft);
  --tnt-accent-medium:      var(--tnt-primary-medium);
  --tnt-accent-glow:        var(--tnt-primary-glow);
  --tnt-accent-glow-lg:     color-mix(in srgb, var(--tnt-primary) 48%, transparent);

  /* Warning/yellow — slightly brighter saturation in dark mode
     for legibility against navy bg; text-on-yellow color
     unchanged (always dark amber on yellow surface). */
  --tnt-warning-soft:        color-mix(in srgb, var(--tnt-warning-a) 18%, transparent);
  --tnt-warning-medium:      color-mix(in srgb, var(--tnt-warning-a) 30%, transparent);
  --tnt-warning-glow:        color-mix(in srgb, var(--tnt-warning-a) 42%, transparent);
  --tnt-warning-glow-lg:     color-mix(in srgb, var(--tnt-warning-a) 52%, transparent);

  /* Ambient grid — denser in dark mode so dots read against navy */
  --tnt-ambient-grid-opacity: 0.7;

  /* Glass panel — dark mode tuning.
     Surface mix lowered (more transparency since dark surface
     is less visually heavy). Highlight uses warm tone since
     identity flips warm in dark. */
  --tnt-glass-bg:               color-mix(in srgb, var(--tnt-surface) 70%, transparent);
  --tnt-glass-border:           color-mix(in srgb, var(--tnt-text) 10%, transparent);
  --tnt-glass-highlight:        color-mix(in srgb, var(--tnt-warm-highlight) 30%, transparent);

  /* Component tokens auto-update via role/surface/text/border. */
}

/* ═════════════════════════════════════════════════════════════
   PAGE CHROME
   ═════════════════════════════════════════════════════════════ */
html, body {
  background-color: var(--tnt-bg);
  color: var(--tnt-text);
  transition: var(--tnt-transition-theme);
}

html { scroll-padding-top: var(--tnt-header-offset); }
section[id^="tnt-"] { scroll-margin-top: var(--tnt-header-offset); }

@media (prefers-reduced-motion: reduce) {
  html, body { transition: none; }
}

/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-banner (v2.7)
   Reusable panel-with-rail. Drop any content inside. Rail color
   set by variant. Shimmer travels vertically on the rail.
   ═════════════════════════════════════════════════════════════ */
.tnt-banner {
  position: relative;
  display: block;
  margin-left: auto;
  margin-right: auto;
  padding: var(--tnt-banner-padding);
  background: var(--tnt-banner-bg);
  border: 1px solid var(--tnt-banner-border);
  border-left: none;
  border-radius: var(--tnt-banner-radius);
  box-shadow: var(--tnt-banner-shadow);
  overflow: hidden;
  transition: background 0.3s ease, border-color 0.3s ease;
}

/* Rail — colored stripe on left edge */
.tnt-banner::before {
  content: "";
  position: absolute;
  top: 0; left: 0; bottom: 0;
  width: var(--tnt-banner-rail-width);
  pointer-events: none;
  z-index: 1;
}

/* Shimmer — white gradient sliding up the rail */
.tnt-banner::after {
  content: "";
  position: absolute;
  top: 0; left: 0; bottom: 0;
  width: var(--tnt-banner-rail-width);
  pointer-events: none;
  z-index: 2;
  background: linear-gradient(180deg,
    transparent 0%,
    transparent 40%,
    var(--tnt-banner-shimmer) 50%,
    transparent 60%,
    transparent 100%);
  background-size: 100% 300%;
  background-position: 0 100%;
  animation: tntBannerShimmer var(--tnt-banner-shimmer-duration) ease-in-out infinite;
  mix-blend-mode: overlay;
}
@keyframes tntBannerShimmer {
  0%, 30%   { background-position: 0 100%;  }
  70%, 100% { background-position: 0 -200%; }
}

/* Variant — accent rail (warm light / blue dark) */
.tnt-banner--accent::before { background: var(--tnt-banner-rail-accent); }

/* Variant — identity rail (blue light / warm dark) */
.tnt-banner--identity::before { background: var(--tnt-banner-rail-identity); }

/* Stagger shimmer on stacked banners so they pulse asynchronously */
.tnt-banner + .tnt-banner::after { animation-delay: -1.75s; }
.tnt-banner + .tnt-banner + .tnt-banner::after { animation-delay: -2.5s; }
.tnt-banner + .tnt-banner + .tnt-banner + .tnt-banner::after { animation-delay: -3.25s; }

@media (prefers-reduced-motion: reduce) {
  .tnt-banner::after { animation: none !important; }
  .tnt-banner { transition: none; }
}

/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-brand-text (v2.8)
   Animated gradient wordmark effect. Wrap a word or phrase
   inside any heading to apply the signature brand sweep +
   shimmer. Auto-resolves via --tnt-identity-* tokens so it
   flips correctly per theme.
   ═════════════════════════════════════════════════════════════ */
@property --tnt-brand-text-wipe {
  syntax: '<percentage>';
  initial-value: -140%;
  inherits: false;
}
@property --tnt-brand-text-shim {
  syntax: '<percentage>';
  initial-value: 0%;
  inherits: false;
}

.tnt-brand-text {
  background-image:
    linear-gradient(105deg,
      transparent 30%,
      var(--tnt-identity-highlight) 50%,
      transparent 70%),
    linear-gradient(110deg,
      var(--tnt-identity-a) 0%,
      var(--tnt-identity-b) 25%,
      var(--tnt-identity-a) 50%,
      var(--tnt-identity-b) 75%,
      var(--tnt-identity-a) 100%);
  background-size: 250% 100%, 200% auto;
  background-position: var(--tnt-brand-text-wipe) 0, var(--tnt-brand-text-shim) center;
  background-repeat: no-repeat, repeat;
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  animation:
    tntBrandTextWipe 4.5s ease-in-out infinite,
    tntBrandTextShim 6s   linear       infinite;
}
@keyframes tntBrandTextWipe {
  0%, 15%   { --tnt-brand-text-wipe: -140%; }
  70%, 100% { --tnt-brand-text-wipe:  240%; }
}
@keyframes tntBrandTextShim {
  from { --tnt-brand-text-shim:    0%; }
  to   { --tnt-brand-text-shim: -200%; }
}

@media (prefers-reduced-motion: reduce) {
  .tnt-brand-text { animation: none !important; }
}

/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-beams (v2.8)
   Ambient animated dot-beams for section backgrounds.
   Usage:
     <div class="tnt-beams" aria-hidden="true"></div>
   Two beams emit automatically: horizontal sweep + vertical
   sweep, each with dotted texture. Opposite sweep directions
   create subtle motion without being busy.
   ═════════════════════════════════════════════════════════════ */
.tnt-beams {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

/* Horizontal beam (left → right) */
.tnt-beams::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: var(--tnt-beam-thickness);
  transform: translateY(-50%);
  background-image:
    radial-gradient(
      circle,
      var(--tnt-beam-dot-color) var(--tnt-beam-dot-size),
      transparent calc(var(--tnt-beam-dot-size) + 0.4px)
    );
  background-size: var(--tnt-beam-dot-gap) var(--tnt-beam-dot-gap);
  -webkit-mask-image: var(--tnt-beam-gradient-h);
          mask-image: var(--tnt-beam-gradient-h);
  -webkit-mask-size: 40% 100%;
          mask-size: 40% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: -40% center;
          mask-position: -40% center;
  opacity: var(--tnt-beam-opacity);
  animation: tntBeamH var(--tnt-beam-duration-horizontal) linear infinite;
  will-change: mask-position;
}

/* Vertical beam (top → bottom) */
.tnt-beams::after {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--tnt-beam-thickness);
  height: 100%;
  transform: translateX(-50%);
  background-image:
    radial-gradient(
      circle,
      var(--tnt-beam-dot-color) var(--tnt-beam-dot-size),
      transparent calc(var(--tnt-beam-dot-size) + 0.4px)
    );
  background-size: var(--tnt-beam-dot-gap) var(--tnt-beam-dot-gap);
  -webkit-mask-image: var(--tnt-beam-gradient-v);
          mask-image: var(--tnt-beam-gradient-v);
  -webkit-mask-size: 100% 40%;
          mask-size: 100% 40%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center -40%;
          mask-position: center -40%;
  opacity: var(--tnt-beam-opacity);
  animation: tntBeamV var(--tnt-beam-duration-vertical) linear infinite;
  will-change: mask-position;
}

@keyframes tntBeamH {
  0%   { -webkit-mask-position: -40% center; mask-position: -40% center; }
  100% { -webkit-mask-position: 140% center; mask-position: 140% center; }
}
@keyframes tntBeamV {
  0%   { -webkit-mask-position: center -40%; mask-position: center -40%; }
  100% { -webkit-mask-position: center 140%; mask-position: center 140%; }
}

@media (prefers-reduced-motion: reduce) {
  .tnt-beams::before,
  .tnt-beams::after { animation: none !important; opacity: calc(var(--tnt-beam-opacity) * 0.5); }
}

/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-modal (v2.8)
   Reusable dialog overlay. Scrim + centered card panel.
   Usage:
     <div class="tnt-modal" data-tnt-modal hidden role="dialog" aria-modal="true">
       <div class="tnt-modal__scrim" data-tnt-modal-scrim></div>
       <div class="tnt-modal__panel">
         ...content...
       </div>
     </div>
   Toggle visibility via [hidden] attribute; JS adds
   .tnt-modal--visible for transition.
   ═════════════════════════════════════════════════════════════ */
.tnt-modal {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
.tnt-modal--visible {
  opacity: 1;
  pointer-events: auto;
}

.tnt-modal__scrim {
  position: absolute;
  inset: 0;
  background: var(--tnt-modal-scrim);
  -webkit-backdrop-filter: blur(var(--tnt-modal-scrim-blur));
          backdrop-filter: blur(var(--tnt-modal-scrim-blur));
}

.tnt-modal__panel {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--tnt-modal-max-width);
  padding: var(--tnt-modal-padding);
  background: var(--tnt-modal-bg);
  color: var(--tnt-modal-color);
  border: 1px solid var(--tnt-modal-border);
  border-radius: var(--tnt-modal-radius);
  box-shadow: var(--tnt-modal-shadow);
  transform: scale(0.96) translateY(8px);
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.tnt-modal--visible .tnt-modal__panel {
  transform: scale(1) translateY(0);
}

/* Accent-gradient border treatment (matches toast) */
.tnt-modal__panel::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(120deg,
    var(--tnt-accent-a),
    var(--tnt-accent-b),
    var(--tnt-accent-a));
  background-size: 200% auto;
  animation: tntModalBorder 4s linear infinite;
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  opacity: 0.65;
}
@keyframes tntModalBorder {
  from { background-position: 0% center; }
  to   { background-position: 200% center; }
}

@media (prefers-reduced-motion: reduce) {
  .tnt-modal,
  .tnt-modal__panel { transition: none; }
  .tnt-modal__panel::before { animation: none; }
}

/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-glass-panel (v2.10)
   Tinted-glass wrapper. Place around section content blocks
   to make them sit on a frosted pane that blurs the animated
   bg behind. Content (banners, cards, text) goes inside.

   Usage:
     <section class="tnt-myitem">
       <div class="tnt-myitem__bg" aria-hidden="true">...</div>
       <div class="tnt-myitem__inner">
         <div class="tnt-glass-panel">
           ... heading banner, sub banner, cards, CTAs ...
         </div>
       </div>
     </section>

   Sections may stack multiple panels (e.g., separate panels for
   left visual + right content) when desired.
   ═════════════════════════════════════════════════════════════ */
.tnt-glass-panel {
  position: relative;
  border-radius: var(--tnt-glass-radius);
  padding: var(--tnt-glass-padding);
  /* No background / border / backdrop-filter directly on the
     element — those live on ::before so content can stay at 100%
     opacity while the surface dims on hover. The wrapper itself
     stays transparent so children render in front of the
     pseudo-painted surface. */
  background: transparent;
  isolation: isolate;
  transition: var(--tnt-transition-theme);
}
/* Surface layer — paints the bg, border, blur, and shadow. Hover
   fades JUST this layer, keeping text/buttons/children fully opaque.
   z-index:-1 puts it behind content but above the section's own bg. */
.tnt-glass-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--tnt-glass-bg);
  border: 1px solid var(--tnt-glass-border);
  box-shadow: var(--tnt-glass-shadow);
  -webkit-backdrop-filter: blur(var(--tnt-glass-blur)) saturate(var(--tnt-glass-saturate));
          backdrop-filter: blur(var(--tnt-glass-blur)) saturate(var(--tnt-glass-saturate));
  transition: opacity 0.25s ease, background 0.3s ease, border-color 0.3s ease;
}
/* Hover: ONLY the surface dims; child content (text, buttons,
   icons) stays at 100% opacity. The aurora bg / page bg shows
   through more clearly. Sections that don't want this affordance
   can set [data-tnt-glass-hover="off"] on the panel. */
.tnt-glass-panel:hover::before {
  opacity: 0.85;
}
.tnt-glass-panel[data-tnt-glass-hover="off"]:hover::before {
  opacity: 1;
}

/* Optional modifier — emphasis variant has slightly stronger
   tint + inner glow accent. Use for hero panels that need to
   stand out from secondary content panels. */
.tnt-glass-panel--strong {
  --tnt-glass-bg: color-mix(in srgb, var(--tnt-surface) 88%, transparent);
}

/* Fallback for browsers without backdrop-filter — increase the
   bg opacity so content stays readable without blur. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .tnt-glass-panel::before {
    background: color-mix(in srgb, var(--tnt-surface) 96%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .tnt-glass-panel,
  .tnt-glass-panel::before { transition: none; }
}

/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-feature-card (v2.11)
   Reusable card frame for grids of hoverable items.

   Usage:
     <article class="tnt-feature-card">
       ... your icon, title, description, etc ...
     </article>

   Sections own the internal layout. Card just gives:
     - Surface bg, border, radius, base shadow
     - Hover lift + shadow boost + border color shift
     - Signature top-edge rail that scales in from center

   Variants:
     .tnt-feature-card--accent  rail uses accent gradient
   ═════════════════════════════════════════════════════════════ */
.tnt-feature-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--tnt-feature-card-padding);
  background: var(--tnt-feature-card-bg);
  border: 1px solid var(--tnt-feature-card-border);
  border-radius: var(--tnt-feature-card-radius);
  box-shadow: var(--tnt-feature-card-shadow);
  overflow: hidden;
  transition: var(--tnt-feature-card-transition);
}

/* Top-edge rail — invisible at rest (scaleX 0), reveals on hover.
   Origin: center → scales out to both edges symmetrically. */
.tnt-feature-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: var(--tnt-feature-card-rail-height);
  background: var(--tnt-feature-card-rail-bg);
  transform: scaleX(0);
  transform-origin: center;
  transition: var(--tnt-feature-card-rail-transition);
  pointer-events: none;
}

.tnt-feature-card:hover,
.tnt-feature-card:focus-visible,
.tnt-feature-card:focus-within {
  transform: translateY(var(--tnt-feature-card-lift));
  box-shadow: var(--tnt-feature-card-shadow-hover);
  border-color: var(--tnt-feature-card-border-hover);
}

.tnt-feature-card:hover::before,
.tnt-feature-card:focus-visible::before,
.tnt-feature-card:focus-within::before {
  transform: scaleX(1);
}

/* Variant — accent rail */
.tnt-feature-card--accent::before {
  background: var(--tnt-feature-card-rail-bg-accent);
}
.tnt-feature-card--accent:hover,
.tnt-feature-card--accent:focus-visible,
.tnt-feature-card--accent:focus-within {
  border-color: var(--tnt-accent-medium);
}

@media (prefers-reduced-motion: reduce) {
  .tnt-feature-card,
  .tnt-feature-card::before { transition: none !important; }
}

/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-confirm-toast (v2.13)
   Pill confirmation-gate toast with paged text + paging-progress dots
   above + thicker rim countdown + corner-docked eyebrow ribbon (when
   user clicks the toast itself) + commit-flash final state (per-button
   confirmation). Created lazily by the JS controller and portaled
   into <body>. Position is set by JS via inline style (top, left).

   Markup created at runtime:
     <div class="tnt-confirm-toast" role="status" aria-live="polite">
       <div class="tnt-confirm-toast__eyebrow">Click the button, not me.</div>
       <div class="tnt-confirm-toast__dots">
         <span class="tnt-confirm-toast__dot tnt-confirm-toast__dot--active"></span>
         <span class="tnt-confirm-toast__dot"></span>
         ...
       </div>
       <span class="tnt-confirm-toast__viewport">
         <span class="tnt-confirm-toast__text">message</span>
       </span>
     </div>

   States added by JS:
     .tnt-confirm-toast--visible    fade in (toast appearance)
     .tnt-confirm-toast--armed      paging finished, rim countdown active
     .tnt-confirm-toast--committing commit flash (final state on confirm)
     .tnt-confirm-toast--scolded    eyebrow ribbon shown (toast was clicked)
   ═════════════════════════════════════════════════════════════ */
.tnt-confirm-toast {
  position: absolute;        /* JS sets top/left after measuring trigger */
  z-index: 9999;
  display: inline-flex;
  flex-direction: column;     /* dots on top, viewport below */
  align-items: stretch;
  gap: 0.4rem;
  min-width: var(--tnt-confirm-toast-min-width);
  max-width: var(--tnt-confirm-toast-max-width);
  padding: var(--tnt-confirm-toast-padding);
  background: var(--tnt-confirm-toast-bg);
  color: var(--tnt-confirm-toast-color);
  border: 1px solid var(--tnt-confirm-toast-border);
  border-radius: var(--tnt-confirm-toast-radius);
  box-shadow: var(--tnt-confirm-toast-shadow);
  font-family: var(--tnt-font-ui);
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0.01em;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(4px);
  pointer-events: none;
  transition:
    opacity   var(--tnt-confirm-toast-fade-out) ease,
    transform var(--tnt-confirm-toast-fade-out) ease,
    background 200ms ease,
    color      200ms ease;
  cursor: default;
  text-align: center;
}

.tnt-confirm-toast--visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
  transition:
    opacity   var(--tnt-confirm-toast-fade-in) ease,
    transform var(--tnt-confirm-toast-fade-in) ease,
    background 200ms ease,
    color      200ms ease;
}

/* Pill rim countdown — thicker, clearer. Implemented as a conic-gradient
   ::before. Suppressed during paging; activated by .--armed when paging
   completes. Suppressed again during .--committing for clean exit. */
.tnt-confirm-toast::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--tnt-confirm-toast-rim-thickness));
  border-radius: inherit;
  padding: var(--tnt-confirm-toast-rim-thickness);
  background: conic-gradient(
    from -90deg,
    var(--tnt-confirm-toast-rim) 0%,
    var(--tnt-confirm-toast-rim) var(--tnt-confirm-toast-progress, 0%),
    transparent var(--tnt-confirm-toast-progress, 0%),
    transparent 100%);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms ease;
}
.tnt-confirm-toast--armed::before {
  opacity: 1;
  animation: tntConfirmToastProgress var(--tnt-confirm-toast-duration) linear forwards;
}
.tnt-confirm-toast--committing::before {
  opacity: 0;
  animation: none;
}
@property --tnt-confirm-toast-progress {
  syntax: '<percentage>';
  initial-value: 0%;
  inherits: false;
}
@keyframes tntConfirmToastProgress {
  from { --tnt-confirm-toast-progress: 0%;   }
  to   { --tnt-confirm-toast-progress: 100%; }
}

/* ─── PAGING DOTS row ────────────────────────────────────────────
   Sits above the text viewport. One dot per page; the active dot
   fills with the rim color, completed dots stay filled. Bootstrap
   creates this row in JS so its size matches the page count. */
.tnt-confirm-toast__dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--tnt-confirm-toast-dots-gap);
  min-height: var(--tnt-confirm-toast-dot-size);
}
.tnt-confirm-toast__dots:empty { display: none; }

.tnt-confirm-toast__dot {
  width:  var(--tnt-confirm-toast-dot-size);
  height: var(--tnt-confirm-toast-dot-size);
  border-radius: 50%;
  background: var(--tnt-border-strong);
  transition: background 240ms ease, transform 240ms ease;
  flex-shrink: 0;
}
.tnt-confirm-toast__dot--done {
  background: var(--tnt-confirm-toast-rim);
}
.tnt-confirm-toast__dot--active {
  background: var(--tnt-confirm-toast-rim);
  transform: scale(1.3);
}

/* ─── EYEBROW ribbon ─────────────────────────────────────────────
   Small attached pill docked to the top-right corner of the main
   toast. Briefly shown when the user mistakenly clicks the toast
   body instead of the trigger. Doesn't cover the chunk, doesn't
   interrupt paging — fills the corner like a Mac notification on
   top of an open window. Auto-fades after SCOLD_HOLD ms. */
.tnt-confirm-toast__eyebrow {
  position: absolute;
  top: calc(-1 * var(--tnt-confirm-toast-rim-thickness) - 0.6rem);
  right: 1rem;
  transform: translateY(-100%);
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.7rem;
  background: var(--tnt-accent-a);
  color: #fff;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-style: italic;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  box-shadow: 0 4px 14px -4px rgba(0,0,0,0.3);
  opacity: 0;
  pointer-events: none;
  transition:
    opacity 200ms ease,
    transform 240ms cubic-bezier(0.34, 1.56, 0.64, 1);
  z-index: 1;
}
.tnt-confirm-toast--scolded .tnt-confirm-toast__eyebrow {
  opacity: 1;
  transform: translateY(-100%) scale(1);
  /* tiny bouncy entrance */
  animation: tntConfirmToastEyebrowPop 280ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes tntConfirmToastEyebrowPop {
  0%   { opacity: 0; transform: translateY(-90%) scale(0.85); }
  100% { opacity: 1; transform: translateY(-100%) scale(1);   }
}

/* ─── Viewport ──────────────────────────────────────────────────
   Centers the chunk both axes. No edge mask — text renders crisp. */
.tnt-confirm-toast__viewport {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  white-space: nowrap;
  text-align: center;
}

/* ─── Text element — paginated by JS ────────────────────────────
   Each "page" is the textContent at a moment in time; transitions
   opacity + a tiny vertical lift on swap. */
.tnt-confirm-toast__text {
  display: inline-block;
  opacity: 1;
  transform: translateY(0);
  text-align: center;
  transition:
    opacity   var(--tnt-confirm-toast-page-fade, 240ms) ease,
    transform var(--tnt-confirm-toast-page-fade, 240ms) ease;
}
.tnt-confirm-toast__text--swapping {
  opacity: 0;
  transform: translateY(-2px);
}

/* The "click to confirm" prompt page — slightly emphasized so the
   user knows they've reached the actionable moment. */
.tnt-confirm-toast--armed .tnt-confirm-toast__text {
  font-weight: 600;
}

/* ─── COMMIT-FLASH state ────────────────────────────────────────
   Briefly shown after the user confirms. The toast inverts to the
   accent color so the moment of commit feels punctuated — a beat
   before navigation. Per-button text is set by section attributes. */
.tnt-confirm-toast--committing {
  background: var(--tnt-accent-a);
  color: #fff;
  border-color: var(--tnt-accent-a);
}
.tnt-confirm-toast--committing .tnt-confirm-toast__dots,
.tnt-confirm-toast--committing .tnt-confirm-toast__eyebrow {
  display: none;
}
.tnt-confirm-toast--committing .tnt-confirm-toast__text {
  font-weight: 700;
}

/* ═════════════════════════════════════════════════════════════
   TOAST TAIL / TICK / GLOW  (v2.22+)
   ─────────────────────────────────────────────────────────────
   Visual indicator pointing from a toast toward its trigger
   element, so users can see what the floating pill is attached
   to. Four variants, selected via body[data-tnt-toast-tail]:

     "tick"          — small triangular notch (default)
     "tick-glow"     — tick + soft pulsing halo
     "tail-line"     — thin ~22px gradient line connecting toast→trigger
     "all"           — tick + glow + line combined

   The engine sets two things on the toast at position time:
     data-tnt-toast-tail-dir="up" | "down"     (which side the tail sits on)
     style="--tnt-toast-tail-x: <px>"          (horizontal alignment)

   "down" = toast is ABOVE trigger, tail points DOWN out of bottom
   "up"   = toast is BELOW trigger, tail points UP out of top

   Sections never style these — section authors just write content.
   The admin can change the variant globally via the plugin's
   "TNT Bootstrap → Toast Style" page.
   ═════════════════════════════════════════════════════════════ */

/* Tick — sharp triangular notch matching toast bg + border.
   Default if body has no [data-tnt-toast-tail] attribute. */
.tnt-confirm-toast::after {
  content: "";
  position: absolute;
  left: var(--tnt-toast-tail-x, 50%);
  width: 12px;
  height: 12px;
  background: var(--tnt-confirm-toast-bg);
  border: 1px solid var(--tnt-confirm-toast-border);
  /* Show only two adjacent edges — bottom+right rotated 45° = a downward tick */
  pointer-events: none;
  transform: translateX(-50%) rotate(45deg);
  transition: background 200ms ease, border-color 200ms ease;
}
/* Tail down (toast above trigger, tick at bottom) */
.tnt-confirm-toast[data-tnt-toast-tail-dir="down"]::after {
  bottom: -7px;
  border-top: none;
  border-left: none;
}
/* Tail up (toast below trigger, tick at top) */
.tnt-confirm-toast[data-tnt-toast-tail-dir="up"]::after {
  top: -7px;
  border-bottom: none;
  border-right: none;
}

/* Variant: tick-glow — adds a soft pulsing halo behind the tick.
   The halo emphasizes the tail's pointer role with a gentle pulse. */
body[data-tnt-toast-tail="tick-glow"] .tnt-confirm-toast,
body[data-tnt-toast-tail="all"]       .tnt-confirm-toast {
  /* Halo as a pseudo-element on the toast itself, on the side
     facing the trigger. Implemented as a radial-gradient layered
     on top of the existing bg using box-shadow targeting that side. */
}
body[data-tnt-toast-tail="tick-glow"] .tnt-confirm-toast::before,
body[data-tnt-toast-tail="all"]       .tnt-confirm-toast::before {
  /* Override the existing animated border ring with one that also
     glows on the trigger-facing side. We layer the original armed
     ring (--armed) above this when armed. The base treatment here
     keeps the existing pseudo while adding the glow as box-shadow
     on the tick. */
}
body[data-tnt-toast-tail="tick-glow"] .tnt-confirm-toast::after,
body[data-tnt-toast-tail="all"]       .tnt-confirm-toast::after {
  box-shadow:
    0 0 0 0     color-mix(in srgb, var(--tnt-accent-a) 60%, transparent),
    0 0 14px 2px color-mix(in srgb, var(--tnt-accent-a) 35%, transparent);
  animation: tntToastTailPulse 2.2s ease-in-out infinite;
}
@keyframes tntToastTailPulse {
  0%, 100% {
    box-shadow:
      0 0 0 0    color-mix(in srgb, var(--tnt-accent-a) 60%, transparent),
      0 0 8px 1px color-mix(in srgb, var(--tnt-accent-a) 25%, transparent);
  }
  50% {
    box-shadow:
      0 0 0 4px  color-mix(in srgb, var(--tnt-accent-a) 35%, transparent),
      0 0 18px 4px color-mix(in srgb, var(--tnt-accent-a) 45%, transparent);
  }
}

/* Variant: tail-line — a thin animated gradient line running from
   the toast edge toward the trigger. Implemented as an SVG-style
   gradient pseudo-element below/above the toast. */
body[data-tnt-toast-tail="tail-line"] .tnt-confirm-toast,
body[data-tnt-toast-tail="all"]       .tnt-confirm-toast {
  /* Reserve room for the line via outline padding — actual line
     uses an additional ::before pseudo. Since ::before is taken
     by the armed-state ring, we use a wrapper trick: a CSS
     custom-property-driven background extension on ::after. */
}
body[data-tnt-toast-tail="tail-line"] .tnt-confirm-toast[data-tnt-toast-tail-dir="down"],
body[data-tnt-toast-tail="all"]       .tnt-confirm-toast[data-tnt-toast-tail-dir="down"] {
  margin-bottom: 22px;  /* room for the line below */
}
body[data-tnt-toast-tail="tail-line"] .tnt-confirm-toast[data-tnt-toast-tail-dir="up"],
body[data-tnt-toast-tail="all"]       .tnt-confirm-toast[data-tnt-toast-tail-dir="up"] {
  margin-top: 22px;
}
/* The line itself — separate pseudo-element using outline, since
   ::before/::after are claimed. We append a 2px column attached to
   the tick. */
body[data-tnt-toast-tail="tail-line"] .tnt-confirm-toast::after,
body[data-tnt-toast-tail="all"]       .tnt-confirm-toast::after {
  /* Inherits tick styling from base. Add a connecting line beneath
     the tick using a downward-pointing background overflow. */
  background:
    var(--tnt-confirm-toast-bg);
  /* We can't paint a line outside the ::after's box without breaking
     the rotated tick. So we use a sibling effect via box-shadow
     elongation along the perpendicular axis. For "down" tail: shadow
     extends downward-right (since rotated 45°, that becomes "down"
     in screen space). For "up" tail: shadow extends upward-left. */
}
body[data-tnt-toast-tail="tail-line"] .tnt-confirm-toast[data-tnt-toast-tail-dir="down"]::after,
body[data-tnt-toast-tail="all"]       .tnt-confirm-toast[data-tnt-toast-tail-dir="down"]::after {
  /* Stretch the tick downward as a rectangle: 2px wide, ~24px tall.
     We achieve this by overriding the shape: keep the rotated diamond
     bottom edge, extend backwards via a bottom-positioned linear
     gradient as background-image. */
  background-image: linear-gradient(180deg,
    var(--tnt-confirm-toast-bg) 0%,
    var(--tnt-confirm-toast-bg) 50%,
    transparent 51%);
  /* Add a thin connector line via box-shadow */
  box-shadow:
    0 18px 0 -5px var(--tnt-accent-a);
}

/* Reduced motion — disable tail glow animation */
@media (prefers-reduced-motion: reduce) {
  body[data-tnt-toast-tail="tick-glow"] .tnt-confirm-toast::after,
  body[data-tnt-toast-tail="all"]       .tnt-confirm-toast::after {
    animation: none !important;
  }
  .tnt-confirm-toast { transition: opacity 0.15s ease; }
  .tnt-confirm-toast--armed::before { animation: none !important; opacity: 1; }
  .tnt-confirm-toast__text { transition: opacity 0.1s ease !important; transform: none !important; }
  .tnt-confirm-toast__dot { transition: none !important; }
  .tnt-confirm-toast__viewport {
    overflow: visible;
    white-space: normal;
  }
}



/* ═════════════════════════════════════════════════════════════
   PRE-ACTION GATE  (v2.23+)
   Visual treatments tied to gate state. Engine logic lives in
   tnt-bootstrap.js (PRE-ACTION GATE ENGINE).
   ═════════════════════════════════════════════════════════════ */

/* Active gate — sections can use this to dim the button so it
   reads as "not ready yet." Sections owning their own button
   styles can override or remove this. */
[data-tnt-gate-active] {
  cursor: not-allowed !important;
  filter: saturate(0.6);
}
[data-tnt-gate-active]:hover,
[data-tnt-gate-active]:focus-visible {
  /* Don't lift on hover when gated — feels broken otherwise */
  transform: none !important;
}

/* Flashing — text has just been swapped to the gate message.
   Use a subtle pulse so the user notices the swap clearly. */
.tnt-gate-flashing {
  animation: tntGateFlash 0.32s ease-out;
}
@keyframes tntGateFlash {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.04); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .tnt-gate-flashing { animation: none !important; }
}


/* ═════════════════════════════════════════════════════════════
   CONFIRM-TOAST ARMED-STATE TRIGGER TREATMENT  (v2.24+)
   ─────────────────────────────────────────────────────────────
   When a button has armed its confirm-toast (the user's first
   click landed; we're waiting for the second), the engine sets
   [data-tnt-confirm-armed="1"] on that button. The button text
   has been swapped to the prompt phrase (e.g. "Click to confirm").

   Default visual treatment globally:
     • Suppress hover-darkening / brightness-down filters
     • Soft pulsing glow ring around the button so it reads as
       "I'm waiting for your second click"
     • A subtle scale-up pulse (~2s cycle)

   Sections can override (set their own [data-tnt-confirm-armed]
   styles in their scoped CSS) to take ownership of the look.

   ADMIN: a future "Bootstrap Customization" admin page will let
   site admins tweak pulse intensity / disable / pick variants.
   ═════════════════════════════════════════════════════════════ */
[data-tnt-confirm-armed] {
  /* Override any section/global filter that dims on hover. */
  filter: none !important;
}
[data-tnt-confirm-armed]:hover,
[data-tnt-confirm-armed]:focus-visible {
  filter: none !important;
}

/* Pulse glow — a layered box-shadow that rings the trigger softly.
   Uses --tnt-accent-glow if set, falls back to a neutral ring.
   Sections that already apply box-shadow can opt out by setting
   their own [data-tnt-confirm-armed] rule. */
[data-tnt-confirm-armed]:not(.tnt-confirm-toast):not([data-tnt-confirm-armed-quiet]) {
  animation: tntConfirmArmedPulse 2.0s ease-in-out infinite;
  z-index: 1;
}
@keyframes tntConfirmArmedPulse {
  0%, 100% {
    box-shadow:
      0 0 0 0     color-mix(in srgb, var(--tnt-accent-a) 50%, transparent),
      0 0 0 0     color-mix(in srgb, var(--tnt-accent-a) 30%, transparent);
  }
  50% {
    box-shadow:
      0 0 0 3px   color-mix(in srgb, var(--tnt-accent-a) 35%, transparent),
      0 0 16px 6px color-mix(in srgb, var(--tnt-accent-a) 22%, transparent);
  }
}
@media (prefers-reduced-motion: reduce) {
  [data-tnt-confirm-armed]:not(.tnt-confirm-toast):not([data-tnt-confirm-armed-quiet]) {
    animation: none !important;
    box-shadow:
      0 0 0 2px   color-mix(in srgb, var(--tnt-accent-a) 35%, transparent) !important;
  }
}


/* ═════════════════════════════════════════════════════════════
   CTA CLUSTER  (v2.24+)
   ─────────────────────────────────────────────────────────────
   Flex-wrap container with intelligent "lonely button" centering.
   When N buttons fit on the same row, they sit left-aligned (or
   wherever the section sets justify-content). When the LAST button
   wraps to a row by itself, it centers in that row. On small
   viewports (<= --tnt-bp-sm), every button takes full width.

   APPROACH: pure CSS. Each child reserves a min-width so it never
   gets too small; flex-wrap lays them out; we use a JS-free
   pattern of "if the last child is flex-grown alone, center it"
   via :only-child-on-its-row detection — but CSS can't detect
   that natively. So we use a simpler rule: when total available
   width can't fit all children side-by-side, wrap; the cluster
   centers all children. Sections call out their alignment with
   data-tnt-cta-cluster-align="start|center|end" if needed.

   The "lonely centering" effect comes from the cluster being
   center-justified by default — solo wraps end up centered for
   free.

   MARKUP:
     <div class="tnt-cta-cluster" data-tnt-cta-cluster-align="start">
       <a class="...">Primary</a>
       <a class="...">Secondary</a>
       <button class="...">More Info</button>
     </div>
   ═════════════════════════════════════════════════════════════ */
.tnt-cta-cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: 0.875rem;
  /* Each child can't shrink below its content; ensures wrap
     happens cleanly when there's not enough horizontal room. */
}
.tnt-cta-cluster > * { flex: 0 0 auto; }

.tnt-cta-cluster[data-tnt-cta-cluster-align="center"] {
  justify-content: center;
}
.tnt-cta-cluster[data-tnt-cta-cluster-align="end"] {
  justify-content: flex-end;
}

/* When wrap occurs, the cluster picks "center" so a lone button
   on its own row is naturally centered. Implementation note: this
   is the "default justify-content collapse" behavior — when there's
   only one child on a row, justify-content:flex-start would stick
   it to the left. Instead we let JS toggle a "wrapped" class.

   Pure-CSS fallback: use justify-content:space-evenly which makes
   1-child rows centered, 2-child rows split nicely, and 3-child
   rows still even. */
.tnt-cta-cluster:not([data-tnt-cta-cluster-align]) {
  justify-content: flex-start;
}
.tnt-cta-cluster.tnt-cta-cluster--wrapped {
  justify-content: center;
}

/* Small viewports: every CTA full-width, vertical stack. */
@media (max-width: 480px) {
  .tnt-cta-cluster > * {
    flex: 1 1 100%;
    width: 100%;
    justify-content: center;
  }
  /* Make sure inline-flex CTAs span full row */
  .tnt-cta-cluster > a,
  .tnt-cta-cluster > button {
    display: flex;
    width: 100%;
  }
}


/* ═════════════════════════════════════════════════════════════
   UNIVERSAL HOVER NON-DIMMING  (v2.25+)
   ─────────────────────────────────────────────────────────────
   No TNT button should ever DIM (lose opacity/saturation/
   brightness) on hover. Hover affordances should brighten or
   shift color, not fade. This rule stops accidental dimming
   from inherited rules (like a parent panel's hover rule)
   from cascading to button children.

   Sections that need a custom hover dim (rare, usually wrong)
   can opt back in by setting [data-tnt-allow-hover-dim] on
   the button itself.

   Selector list covers every TNT button class family + common
   patterns. The :hover state explicitly resets opacity/filter
   so even if an ancestor set them, the button stays at full
   strength.
   ═════════════════════════════════════════════════════════════ */
:where(
  button,
  a,
  [role="button"]
):where(
  .tnt-info-cta,
  .tnt-fb__btn,
  [class*="__cta"],
  [class*="__btn"],
  .tnt-press,
  [class*="tnt-btn"]
):not([data-tnt-allow-hover-dim]):hover,
:where(
  button,
  a,
  [role="button"]
):where(
  .tnt-info-cta,
  .tnt-fb__btn,
  [class*="__cta"],
  [class*="__btn"],
  .tnt-press,
  [class*="tnt-btn"]
):not([data-tnt-allow-hover-dim]):focus-visible {
  opacity: 1 !important;
}

/* Also explicitly counter desaturate / brightness-down filters that
   may leak from ancestors (e.g. a hovered glass panel using
   filter:brightness on its descendants). The button is allowed to
   set its own filter for hover affordance — this just prevents
   inherited dimming from parent hover rules. */
:where(
  button,
  a,
  [role="button"]
):where(
  .tnt-info-cta,
  .tnt-fb__btn,
  [class*="__cta"],
  [class*="__btn"]
):not([data-tnt-allow-hover-dim]) {
  /* This isn't a hover rule — it just makes sure the button has its
     own filter context so a parent's hover filter doesn't cascade. */
  isolation: isolate;
}


/* ═════════════════════════════════════════════════════════════
   BUTTON PRESS ANIMATION (v2.16+)
   Global :active treatment that applies to every TNT button
   tier. Sections using --tnt-btn-* token bundles get this for
   free; sections building custom buttons can opt in by adding
   .tnt-press to the button (or by simply using the :active
   selector pattern on their own classes).

   Effect:
     • On :active, button presses down + scales slightly
     • A quick radial ring pulses outward via box-shadow
     • Releases bouncy (cubic-bezier overshoot) for tactile feel

   Combines cleanly with existing hover-lift transforms by
   chaining transform values (translateY + scale).

   Exposed tokens (above):
     --tnt-btn-press-translate (1px)
     --tnt-btn-press-scale     (0.985)
     --tnt-btn-press-duration  (90ms)
     --tnt-btn-press-release   (180ms)
     --tnt-btn-press-ring-color
   ═════════════════════════════════════════════════════════════ */

/* Generic catch-all: any element using TNT button-tier classes
   participates in the press animation. The selector list covers
   all known TNT button consumers; sections adding new button-like
   elements can either match these classes or opt in via .tnt-press.
   Active-state visual: down-press + scale + ring pulse via shadow. */
:where(
  .tnt-superhero__cta,
  .tnt-superhero__theme-toggle,
  .tnt-superhero__tag,
  .tnt-info-cta,
  .tnt-fb__btn,
  .tnt-prompt__btn,
  .tnt-press
):active {
  transform: translateY(var(--tnt-btn-press-translate)) scale(var(--tnt-btn-press-scale));
  transition: transform var(--tnt-btn-press-duration) ease-out;
  /* Add a quick ring pulse — uses currentColor mix so it picks up
     each button's tier color automatically. The outer ring sits at
     full radius then fades on release. */
  box-shadow:
    0 0 0 0.5rem var(--tnt-btn-press-ring-color),
    inset 0 1px 2px rgba(0, 0, 0, 0.12);
}

/* Release transition is bouncy/snappy via overshoot easing.
   Applied as the default transform/shadow transition for these
   button consumers — overrides the generic --tnt-btn-transition
   on the transform property only, keeping bg/color/border at their
   linear timings. */
:where(
  .tnt-superhero__cta,
  .tnt-superhero__theme-toggle,
  .tnt-superhero__tag,
  .tnt-info-cta,
  .tnt-fb__btn,
  .tnt-prompt__btn,
  .tnt-press
) {
  transition:
    transform   var(--tnt-btn-press-release) cubic-bezier(0.34, 1.56, 0.64, 1),
    box-shadow  0.22s ease,
    background  0.25s ease,
    color       0.2s ease,
    border-color 0.2s ease;
}

@media (prefers-reduced-motion: reduce) {
  :where(
    .tnt-superhero__cta,
    .tnt-superhero__theme-toggle,
    .tnt-superhero__tag,
    .tnt-info-cta,
    .tnt-fb__btn,
    .tnt-prompt__btn,
    .tnt-press
  ):active {
    transform: none;
  }
}

/* ─── Confirm-Toast Button Text Flash (v2.16+) ───
   When the engine arms a confirm-toast trigger, it sets the
   .tnt-confirm-flash class on the button for ~1s. CSS handles
   the eye-catching pulse animation while the engine swaps the
   text content. */
.tnt-confirm-flash {
  /* Two-phase animation: initial eye-catching bounce, then a
     subtle breathing pulse that loops for the full toast lifetime
     (engine reverts the class on disarm/commit). */
  animation:
    tntConfirmFlashIntro    0.95s cubic-bezier(0.34, 1.56, 0.64, 1) 1 forwards,
    tntConfirmFlashBreathe  2.4s ease-in-out 0.95s infinite;
}
@keyframes tntConfirmFlashIntro {
  0%   { transform: scale(1);    filter: brightness(1); }
  20%  { transform: scale(1.06); filter: brightness(1.18); }
  45%  { transform: scale(0.98); filter: brightness(1.08); }
  70%  { transform: scale(1.03); filter: brightness(1.12); }
  100% { transform: scale(1);    filter: brightness(1.06); }
}
@keyframes tntConfirmFlashBreathe {
  0%, 100% { transform: scale(1);     filter: brightness(1.06); }
  50%      { transform: scale(1.015); filter: brightness(1.13); }
}
/* When a button is mid-flash AND being pressed, let press win. */
.tnt-confirm-flash:active {
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  .tnt-confirm-flash { animation: none; }
}


/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-info-cta (v2.12)
   Yellow-tier "More Info" button. Starts as icon + label,
   collapses to icon-only after a settle period. Hover or
   focus restores the label. JS toggles .tnt-info-cta--collapsed
   after data-tnt-confirm-collapse-delay (default 4s).

   Composes with a button base styled via --tnt-btn-warning-*.
   ═════════════════════════════════════════════════════════════ */
.tnt-info-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: var(--tnt-info-cta-padding);
  border-radius: var(--tnt-info-cta-radius);
  font-family: var(--tnt-font-ui);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-decoration: none;
  background: var(--tnt-btn-warning-bg);
  color: var(--tnt-btn-warning-color);
  border: 1px solid var(--tnt-btn-warning-border);
  box-shadow: var(--tnt-btn-warning-shadow);
  cursor: pointer;
  transition: var(--tnt-btn-transition),
              padding 0.4s ease;
}
.tnt-info-cta__icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
}
.tnt-info-cta__label {
  display: inline-block;
  max-width: 6em;
  overflow: hidden;
  white-space: nowrap;
  opacity: 1;
  transition: var(--tnt-info-cta-label-transition);
}
.tnt-info-cta:hover,
.tnt-info-cta:focus-visible {
  background: var(--tnt-btn-warning-bg-hover);
  color: var(--tnt-btn-warning-color-hover);
  border-color: var(--tnt-btn-warning-border-hover);
  transform: translateY(-2px);
  box-shadow: var(--tnt-btn-warning-shadow-hover);
}
.tnt-info-cta:focus-visible {
  outline: 2px solid var(--tnt-warning-b);
  outline-offset: 3px;
}

/* Collapsed state: label shrinks to 0 width + opacity 0,
   button tightens to icon-only padding. */
.tnt-info-cta--collapsed {
  padding: var(--tnt-info-cta-padding-collapsed);
}
.tnt-info-cta--collapsed .tnt-info-cta__label {
  max-width: 0;
  opacity: 0;
  margin: 0;
}
/* On hover/focus while collapsed, label re-expands as a tooltip */
.tnt-info-cta--collapsed:hover .tnt-info-cta__label,
.tnt-info-cta--collapsed:focus-visible .tnt-info-cta__label {
  max-width: 6em;
  opacity: 1;
  margin-left: 0.4rem;
}
.tnt-info-cta--collapsed:hover,
.tnt-info-cta--collapsed:focus-visible {
  padding: var(--tnt-info-cta-padding);
}

@media (prefers-reduced-motion: reduce) {
  .tnt-info-cta,
  .tnt-info-cta__label { transition: none !important; }
}

/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-info-popup (v2.12)
   Modal overlay shown when a .tnt-info-cta is clicked.
   Builds on .tnt-modal with a yellow border treatment so it
   reads as same family as the More Info button.

   Markup is created at runtime by the controller; per-section
   content comes from <template id="tnt-info-{section}">.
   ═════════════════════════════════════════════════════════════ */
.tnt-info-popup {
  /* inherits .tnt-modal positioning + scrim */
}
.tnt-info-popup .tnt-modal__panel {
  max-width: 520px;
  border: 2px solid var(--tnt-info-popup-border);
}
.tnt-info-popup .tnt-modal__panel::before {
  /* Replace .tnt-modal's accent border with warning gradient */
  background: linear-gradient(120deg,
    var(--tnt-warning-a),
    var(--tnt-warning-b),
    var(--tnt-warning-a));
  background-size: 200% auto;
}

.tnt-info-popup__header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0 0 1rem;
}
.tnt-info-popup__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  flex-shrink: 0;
  background: var(--tnt-warning-soft);
  border: 1px solid var(--tnt-warning-medium);
  border-radius: 50%;
  color: var(--tnt-warning-b);
}
.tnt-info-popup__title {
  margin: 0;
  font-family: var(--tnt-font-heading);
  font-size: 1.125rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--tnt-text);
  flex: 1;
}
.tnt-info-popup__close {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 50%;
  color: var(--tnt-text-muted);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}
.tnt-info-popup__close:hover,
.tnt-info-popup__close:focus-visible {
  background: var(--tnt-surface-2);
  color: var(--tnt-text);
}

.tnt-info-popup__body {
  font-family: var(--tnt-font-body);
  font-size: 0.9375rem;
  line-height: 1.65;
  color: var(--tnt-text-muted);
}
.tnt-info-popup__body :where(p) { margin: 0 0 0.75rem; }
.tnt-info-popup__body :where(p:last-child) { margin-bottom: 0; }
.tnt-info-popup__body :where(strong) { color: var(--tnt-text); font-weight: 600; }
.tnt-info-popup__body :where(em) {
  font-style: italic;
  color: var(--tnt-warning-b);
  font-weight: 500;
}
.tnt-info-popup__body :where(h3) {
  margin: 1.25rem 0 0.5rem;
  font-family: var(--tnt-font-heading);
  font-size: 1rem;
  font-weight: 700;
  color: var(--tnt-text);
}
.tnt-info-popup__body :where(ul) {
  margin: 0 0 0.75rem;
  padding-left: 1.25rem;
}
.tnt-info-popup__body :where(li) { margin: 0 0 0.375rem; }


/* ═════════════════════════════════════════════════════════════
   INFO POPUP SLIDES + VERSION BADGE + DEV LINK  (v2.22+)
   ─────────────────────────────────────────────────────────────
   Multi-slide deck inside .tnt-info-popup. Two slides exist by
   default — "main" (section template content) and "dev"
   (auto-augmented "From the Developer" notes from the optional
   <template id="tnt-info-{key}-dev">).

   The deck horizontally translates to switch slides:
     deck[data-tnt-info-slide="main"] → translateX(0)
     deck[data-tnt-info-slide="dev"]  → translateX(-50%)
   ═════════════════════════════════════════════════════════════ */

.tnt-info-popup__title-wrap {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

/* Version badge in popup header */
.tnt-info-popup__version {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.55rem;
  font-family: var(--tnt-font-ui);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--tnt-text-muted);
  background: var(--tnt-surface-2);
  border: 1px solid var(--tnt-border);
  border-radius: 999px;
  white-space: nowrap;
}

/* Slide deck — horizontal track with two slides side by side. */
.tnt-info-popup__deck {
  position: relative;
  overflow: hidden;
  display: grid;
  /* Two slides at 100% width each */
  grid-template-columns: 100% 100%;
  width: 100%;
  transition: transform 320ms cubic-bezier(0.4, 0.0, 0.2, 1);
  /* default = on main slide */
  transform: translateX(0);
}
.tnt-info-popup__deck[data-tnt-info-slide="dev"] {
  transform: translateX(-50%);
}

/* Each slide takes one column of the deck. */
.tnt-info-popup__slide {
  width: 100%;
  min-width: 0;  /* prevents grid blow-out */
  /* Stack content vertically inside the slide */
  display: flex;
  flex-direction: column;
}

/* Inactive slide — hide from a11y when off-screen */
.tnt-info-popup__deck[data-tnt-info-slide="main"] .tnt-info-popup__slide[data-slide="dev"],
.tnt-info-popup__deck[data-tnt-info-slide="dev"]  .tnt-info-popup__slide[data-slide="main"] {
  visibility: hidden;
  transition: visibility 0s linear 320ms;
}

/* Dev link footer button (on main slide) */
.tnt-info-popup__dev-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: 1rem 0 0;
  padding: 0.6rem 1rem;
  font-family: var(--tnt-font-ui);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--tnt-text-muted);
  background: var(--tnt-surface-2);
  border: 1px solid var(--tnt-border);
  border-radius: var(--tnt-btn-radius, 8px);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.tnt-info-popup__dev-link:hover,
.tnt-info-popup__dev-link:focus-visible {
  color: var(--tnt-text);
  background: var(--tnt-surface);
  border-color: var(--tnt-warning-medium);
}
.tnt-info-popup__dev-link svg:first-child { opacity: 0.5; }
.tnt-info-popup__dev-link svg:last-child  { opacity: 0.7; }

/* Back link on dev slide */
.tnt-info-popup__back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  align-self: flex-start;
  margin: 0 0 0.75rem;
  padding: 0.35rem 0.7rem;
  font-family: var(--tnt-font-ui);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--tnt-text-muted);
  background: transparent;
  border: 1px solid var(--tnt-border);
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}
.tnt-info-popup__back-link:hover,
.tnt-info-popup__back-link:focus-visible {
  color: var(--tnt-text);
  background: var(--tnt-surface-2);
}

/* Dev slide heading + version chip */
.tnt-info-popup__dev-heading {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 0.75rem;
  flex-wrap: wrap;
}
.tnt-info-popup__dev-label {
  font-family: var(--tnt-font-ui);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tnt-warning-b);
}
.tnt-info-popup__dev-version {
  display: inline-flex;
  align-items: center;
  padding: 0.18rem 0.5rem;
  font-family: var(--tnt-font-ui);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--tnt-warning-b);
  background: var(--tnt-warning-soft);
  border: 1px solid var(--tnt-warning-medium);
  border-radius: 999px;
}
.tnt-info-popup__dev-placeholder {
  font-style: italic;
  color: var(--tnt-text-muted);
}

/* Reduced motion — slide swap with no transition */
@media (prefers-reduced-motion: reduce) {
  .tnt-info-popup__deck { transition: none !important; }
}


/* ═════════════════════════════════════════════════════════════
   COMPONENT — .tnt-prompt (v2.15)
   Modal-style overlay used for branching decisions ("are you
   sure?", "did you forget X?"). Distinct from .tnt-confirm-toast:
     - confirm-toast = confirmation gate before a known action
     - prompt        = branching decision with multiple paths

   AUDIENCE: Section developers triggering pre-action prompts.

   USAGE — section JS calls window.tntPrompt(opts) where opts:
     {
       title:    "Add a comment first?",
       message:  "A word or two tunes the next iteration. Your call.",
       actions: [
         { label: "Add comment", primary: true,  result: "add" },
         { label: "Send anyway", primary: false, result: "send" }
       ]
     }
   Returns a Promise resolving to the chosen action's `result`,
   or null if dismissed (Esc / scrim click / Cancel).

   The component is built lazily by the engine at first call,
   portaled to <body>, and reused across all sections. Pure
   styling template here; behavior in tnt-bootstrap.js.

   STATES added by JS:
     .tnt-prompt--visible    — fade in
     (none for committing — calling code closes after action)
   ═════════════════════════════════════════════════════════════ */
.tnt-prompt-scrim {
  position: fixed;
  inset: 0;
  z-index: 9998;
  background: color-mix(in srgb, var(--tnt-text) 40%, transparent);
  -webkit-backdrop-filter: blur(4px);
          backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 240ms ease;
}
.tnt-prompt-scrim--visible {
  opacity: 1;
  pointer-events: auto;
}

.tnt-prompt {
  position: fixed;
  z-index: 9999;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.95);
  width: min(420px, calc(100vw - 2rem));
  padding: 1.5rem;
  background: var(--tnt-surface);
  color: var(--tnt-text);
  border: 1px solid var(--tnt-border-strong);
  border-radius: 1rem;
  box-shadow: 0 20px 60px -16px rgba(0,0,0,0.4),
              0 8px 24px -8px rgba(0,0,0,0.2);
  font-family: var(--tnt-font-body);
  opacity: 0;
  pointer-events: none;
  transition:
    opacity   240ms ease,
    transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.tnt-prompt--visible {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  pointer-events: auto;
}

.tnt-prompt__title {
  margin: 0 0 0.6rem;
  font-family: var(--tnt-font-heading);
  font-size: 1.125rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--tnt-text);
  letter-spacing: -0.005em;
}

.tnt-prompt__message {
  margin: 0 0 1.25rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--tnt-text-muted);
}
.tnt-prompt__message strong { color: var(--tnt-text); font-weight: 600; }

.tnt-prompt__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.6rem;
}

.tnt-prompt__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.6rem 1.1rem;
  border-radius: var(--tnt-btn-radius);
  font-family: var(--tnt-font-ui);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  cursor: pointer;
  transition: var(--tnt-btn-transition);
  white-space: nowrap;
}
.tnt-prompt__btn--primary {
  background: var(--tnt-btn-primary-bg);
  color: var(--tnt-btn-primary-color);
  box-shadow: var(--tnt-btn-primary-shadow);
}
.tnt-prompt__btn--primary:hover,
.tnt-prompt__btn--primary:focus-visible {
  transform: translateY(-1px);
  box-shadow: var(--tnt-btn-primary-shadow-hover);
}
.tnt-prompt__btn--secondary {
  background: var(--tnt-btn-secondary-bg);
  color: var(--tnt-btn-secondary-color);
  border-color: var(--tnt-btn-secondary-border);
  box-shadow: var(--tnt-btn-secondary-shadow);
}
.tnt-prompt__btn--secondary:hover,
.tnt-prompt__btn--secondary:focus-visible {
  background: var(--tnt-btn-secondary-bg-hover);
  color: var(--tnt-btn-secondary-color-hover);
  border-color: var(--tnt-btn-secondary-border-hover);
  transform: translateY(-1px);
  box-shadow: var(--tnt-btn-secondary-shadow-hover);
}
.tnt-prompt__btn:focus-visible {
  outline: 2px solid var(--tnt-accent-a);
  outline-offset: 3px;
}

@media (max-width: calc(var(--tnt-bp-sm) - 1px)) {
  .tnt-prompt { padding: 1.25rem; }
  .tnt-prompt__actions {
    flex-direction: column-reverse;
  }
  .tnt-prompt__btn {
    width: 100%;
    padding: 0.75rem 1.1rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tnt-prompt-scrim,
  .tnt-prompt { transition: opacity 0.15s ease; }
}


/* ═════════════════════════════════════════════════════════════
   COMPONENT — Section Stage System (v2.18+, grid-stacking v2.20+)
   ─────────────────────────────────────────────────────────────
   Lets a section host MULTIPLE STAGES as stacked layers, with
   one or more visible at a time. Two stage modes:

     • EXCLUSIVE stages (data-tnt-stage-mode="exclusive", default)
       Only ONE exclusive stage is visible per stack at any time.
       Switching to one hides all sibling exclusives. Used for
       content stages: "default view", "artwork view", "demo view".

     • INDEPENDENT stages (data-tnt-stage-mode="independent")
       Toggle on/off without affecting siblings. Used for layers
       like the section's animated background that should remain
       visible across content-stage swaps but be hideable on demand.

   STACK CONTAINER (v2.20+):
     The .tnt-stage-stack uses CSS GRID STACKING — every stage
     occupies the same grid cell. The cell auto-sizes to the
     tallest stage's NATURAL CONTENT, which solves the responsive
     "cut off" bug where the old absolute-positioning approach
     used a fixed min-height that couldn't grow at narrow widths.

     position: relative   (for child stages)
     isolation: isolate   (for z-index containment)
     display: grid        (the stacking layout)

   MARKUP CONTRACT:
     <div class="tnt-{slug}__stack tnt-stage-stack">
       <!-- Independent (always-on background) layer -->
       <div class="tnt-stage" data-tnt-stage="bg" data-tnt-stage-mode="independent">
         ...animated background...
       </div>
       <!-- Exclusive content stages -->
       <div class="tnt-stage" data-tnt-stage="default" data-tnt-stage-default>
         ...primary content + a switch button to "artwork"...
       </div>
       <div class="tnt-stage" data-tnt-stage="artwork">
         ...artwork view content + a switch button back to "default"...
       </div>
     </div>

   SWITCHER BUTTONS (exclusive swap):
     <button data-tnt-stage-switch="artwork">Show Artwork</button>
     <!-- Inside any stage; switches the parent stack to "artwork" -->

   TOGGLE BUTTONS (independent on/off):
     <button data-tnt-stage-toggle="bg">Hide background</button>
     <!-- Toggles visibility of the stage named "bg" -->

   CROSS-STACK TARGETING:
     <button data-tnt-stage-toggle="bg"
             data-tnt-stage-stack-id="other-stack-id">...</button>
     <!-- Targets a stack other than the closest ancestor -->

   STATES (added by JS):
     .tnt-stage--visible   stage is shown
     .tnt-stage--hidden    stage is hidden (post fade-out)
     .tnt-stage--entering  stage is mid-fade-in (transient)
     .tnt-stage--leaving   stage is mid-fade-out (transient)

   TRANSITION LIBRARY:
     Default transition: FADE (300ms). The stack can override per
     instance via --tnt-stage-transition-duration. More transitions
     can be added to this library over time (slide, instant, etc.).

   AUTHOR NOTES:
     • Mark ONE exclusive stage with data-tnt-stage-default to set
       the initial visible stage. If none is marked, the first
       exclusive stage is used.
     • Independent stages are visible by default. Add
       data-tnt-stage-hidden to start them hidden.
     • The engine manages all visibility class toggling. Sections
       NEVER manipulate .tnt-stage--visible/hidden manually.
   ═════════════════════════════════════════════════════════════ */

.tnt-stage-stack {
  position: relative;
  isolation: isolate;
  --tnt-stage-transition-duration: 300ms;
  /* GRID STACKING: every stage sits in the same grid cell, so the
     cell auto-sizes to the tallest stage's NATURAL content. Solves
     the responsive "section cut off" issue where the old absolute
     positioning + fixed min-height couldn't grow at narrow widths. */
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  align-items: stretch;
}

/* During engine init only, suppress all stage transitions so the
   initial visibility set is instant (no fade-in flash). The engine
   removes this class on the next animation frame after init. */
.tnt-stage-stack--initializing .tnt-stage {
  transition: none !important;
}

.tnt-stage {
  /* Grid stacking: every stage occupies the same cell (1/1). When
     the active stage's content is taller than siblings, the grid
     cell grows; siblings stretch to match but remain hidden. */
  grid-column: 1;
  grid-row: 1;
  /* DEFAULT = HIDDEN until engine init runs. Prevents the FOUC
     where every stage briefly renders on top of each other before
     JS picks the visible one. */
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition:
    opacity   var(--tnt-stage-transition-duration) ease,
    transform var(--tnt-stage-transition-duration) ease;
}

/* Once the engine init has run, state classes take over */
.tnt-stage-stack[data-tnt-stage-init] .tnt-stage {
  /* Inherits transitions; visibility defaults to whatever class
     the engine applied (--visible or --hidden). The base hidden
     defaults above are overridden by --visible. */
}

/* Independent stages (e.g. bg layer) sit BENEATH exclusive stages
   by default. Sections can override z-index per-stage if needed. */
.tnt-stage[data-tnt-stage-mode="independent"] {
  z-index: 0;
}
.tnt-stage:not([data-tnt-stage-mode="independent"]) {
  z-index: 1;
}

/* Hidden state (set by JS after fade-out completes) */
.tnt-stage--hidden {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}

/* Mid-transition: fade-out */
.tnt-stage--leaving {
  opacity: 0;
  pointer-events: none;
}

/* Mid-transition: fade-in */
.tnt-stage--entering {
  opacity: 0;
}

/* Visible (post-entering) — wins over the hidden base */
.tnt-stage--visible {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
  .tnt-stage { transition: opacity 0.1s ease; }
}


/* ═════════════════════════════════════════════════════════════
   COMPONENT — Caption Fader (v2.19+)
   ─────────────────────────────────────────────────────────────
   A compact caption rotator for tight spaces (compact header
   bands, eyebrow banners, status pills). Cycles through multiple
   <span> children inside a slot, fading one in at a time. Same
   visual pattern as the toast paging dots/text but section-owned.

   MARKUP:
     <div class="tnt-caption-fader" data-tnt-caption-fader>
       <span class="tnt-caption-fader__line">First caption.</span>
       <span class="tnt-caption-fader__line">Second caption.</span>
       <span class="tnt-caption-fader__line">Third caption.</span>
     </div>

   OPTIONAL ATTRIBUTES:
     data-tnt-caption-fader-hold="2200"   per-line hold ms (default 2200)
     data-tnt-caption-fader-fade="280"    fade ms (default 280)
     data-tnt-caption-fader-pause-on-hover="0"  disable hover pause

   STATES:
     .tnt-caption-fader__line.is-active   shown (fades in)

   The engine cycles `is-active` through children. Lines stack
   absolutely inside the fader; the fader itself sizes to the
   tallest line. Pauses on hover/focus by default.

   IMPLEMENTATION: see "CAPTION FADER ENGINE" IIFE in tnt-bootstrap.js.
═════════════════════════════════════════════════════════════ */
.tnt-caption-fader {
  position: relative;
  display: block;
  /* Sizes to the tallest line via the visually-stacked layout below */
}
.tnt-caption-fader__line {
  display: block;
  /* All lines stack on top of each other; engine reveals one. */
  opacity: 0;
  transition: opacity var(--tnt-caption-fader-fade, 280ms) ease;
  pointer-events: none;
}
.tnt-caption-fader__line.is-active {
  opacity: 1;
  pointer-events: auto;
}
/* All lines but the active one absolutely-position so they don't
   take vertical space — only the active line drives layout. */
.tnt-caption-fader__line:not(.is-active) {
  position: absolute;
  inset: 0;
}
@media (prefers-reduced-motion: reduce) {
  .tnt-caption-fader__line { transition: opacity 0.05s ease; }
}
