/* ============================================================
   OMG21 — MASTER STYLESHEET
   styles.css

   HOW THIS FILE IS ORGANIZED:
   Scroll top to bottom. Each section has a big label.
   Edit only the section you need. Never hardcode a color — use variables.

   SECTIONS:
   1. FONTS
   2. VARIABLES  ← all colors, fonts, shadows live here
   3. RESET       ← browser default cleanup
   4. BASE        ← html, body, scrollbar
   5. LAYOUT      ← panels, page structure
   6. NAV         ← side navigation
   7. BUTTONS     ← all button types
   8. ACCORDION   ← expand/collapse rows
   9. COPY ROW    ← click-to-copy address rows
   10. TYPE        ← headings, body, labels, BTC values. The type
                     page's frames moved to .dev/04-typography/typography.css
   11. LOGOS       ← sprite-based logo system, full color + mono
   12. FIELDS      ← the quantity and name fields, the four states.
                      Every page starts from these; a page may add
                      work of its own on top but does not redraw them
   13. TESTIMONIAL ← testimonial card + pagination nav
   14. CONTACT     ← contact page layout (title glow + channel stack)
   15. CARDS       ← the product card and its grid
   16. RESPONSIVE  ← the four bands (1440, 1080, 768, 341) and the
                     header's own two edges (900, 480)
============================================================ */


/* ============================================================
   1. FONTS
   All fonts self-hosted from assets/font/ — zero external requests.
   No Google Fonts CDN, no third-party tracking, no IP leaks.

   REQUIRED FILES (download once, drop into assets/font/):
     ocr-bitcoin.woff2
     big-shoulders-v4-latin-800.woff2      (weight 800)
     big-shoulders-v4-latin-900.woff2      (weight 900)
     ibm-plex-mono-light.woff2               (weight 300)
     ibm-plex-mono-regular.woff2             (weight 400)
     ibm-plex-mono-semibold.woff2            (weight 600)
     ibm-plex-mono-semibold-italic.woff2     (weight 600 italic)

   Source: google-webfonts-helper (https://gwfh.mranftl.com/fonts)
   — Latin charset only, woff2 only. gwfh outputs PascalCase names
   (e.g. IBMPlexMono-Regular.woff2) — rename to lowercase-with-dashes
   to match the @font-face urls below. Lowercase = web hygiene +
   Linux deploy safety (Apache is case-sensitive).
============================================================ */

@font-face {
  font-family: 'OCR-Bitcoin';
  src: url('assets/font/ocr-bitcoin.woff2') format('woff2');
  font-weight:  normal;
  font-style:   normal;
  font-display: swap;
}

@font-face {
  font-family: 'Big Shoulders Display';
  src: url('assets/font/big-shoulders-v4-latin-800.woff2') format('woff2');
  font-weight:  800;
  font-style:   normal;
  font-display: swap;
}

@font-face {
  font-family: 'Big Shoulders Display';
  src: url('assets/font/big-shoulders-v4-latin-900.woff2') format('woff2');
  font-weight:  900;
  font-style:   normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Mono';
  src: url('assets/font/ibm-plex-mono-light.woff2') format('woff2');
  font-weight:  300;
  font-style:   normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Mono';
  src: url('assets/font/ibm-plex-mono-regular.woff2') format('woff2');
  font-weight:  400;
  font-style:   normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Mono';
  src: url('assets/font/ibm-plex-mono-semibold.woff2') format('woff2');
  font-weight:  600;
  font-style:   normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Mono';
  src: url('assets/font/ibm-plex-mono-semibold-italic.woff2') format('woff2');
  font-weight:  600;
  font-style:   italic;
  font-display: swap;
}




/* ============================================================
   2. VARIABLES
   These are your design tokens — every color, font, and
   shadow used on the site. Change a value here and it
   updates everywhere automatically.

   TO CHANGE A COLOR: edit the hex value on the right side only.
   Never paste a hex color anywhere else in this file.
============================================================ */

:root {

  /* ── Background ── */
  --bg:            #141210;                      /* page background — darkest */
  --bg-image:      url('/assets/bg/bg-01.webp');  /* panel background image.
                      Root-absolute on purpose: a relative url() inside a
                      custom property is resolved by Chrome against the
                      stylesheet that USES the variable, so a .dev page's own
                      stylesheet asked for .dev/…/assets/bg/bg-01.webp and
                      got a 404 (found 12 September 2026). */

  /* ── Brand Palette (use sparingly — see rules below) ── */
  --orange:        #E3791B;   /* primary brand — one use per element */
  --blue:          #2F35E0;   /* links, hover states, system actions */
  --green:         #32B443;   /* success, confirmed, paid */
  --yellow:        #D8D016;   /* signal / attention / caution */
  --red:           #E03030;   /* error, alert, danger */

  /* ── Color use rules ──
     orange  → primary action button, open accordion, active nav item
     blue    → hover states, links, focused inputs
     green   → success states, confirmed payments
     yellow  → price changes, new/featured badges, caution alerts
     red     → error messages, destructive actions
     ONE accent color per component. Never mix two on the same element.
  ── */

  /* ── Text ── */
  --text-heading:  #F5F3EF;   /* headings and primary text */
  --text-body:     #D8D4CC;   /* body copy */
  --text-label:    #A8A49C;   /* secondary labels, placeholders */

  /* ── Border ── */
  --border:        #2C2926;   /* all borders and dividers */

  /* ── Overlays ── */
  --overlay-20:    rgba(0, 0, 0, 0.20);   /* 20% black wash over content */
  --overlay-50:    rgba(0, 0, 0, 0.50);   /* 50% black wash over content */
  --overlay-70:    rgba(0, 0, 0, 0.70);   /* 70% black wash over content */

  /* ── Surfaces ── */
  --placeholder:   #403D3A;                  /* form input at rest */
  --card:          rgba(26, 23, 20, 0.10);   /* card / panel surface */
  --disabled:      #262320;                  /* disabled state fill */

  /* ── Hover / Active ── */
  --rest:          rgba(245, 243, 239, 0.03);   /* button/nav at rest — near invisible */
  --hover:         rgba(0, 0, 0, 0.10);         /* neutral hover wash */
  --active-orange: rgba(227, 121, 27, 0.10);
  --active-blue:   rgba(47, 53, 224, 0.10);
  --active-green:  rgba(50, 180, 67, 0.10);
  --active-yellow: rgba(216, 208, 22, 0.10);
  --active-red:    rgba(224, 48, 48, 0.10);

  /* ── Shadow ── */
  --shadow:        0 0 12px 3px rgba(0, 0, 0, 0.4);   /* button glow on hover/active */
  --glow-green:        0 0 6px 2px rgba(50, 180, 67, 0.50);   /* active testimonial dot */
  --glow-green-strong: 0 0 6px 2px rgba(50, 180, 67, 0.65);   /* verify dot on hover */

  /* ── Absolutes ── */
  --white:         #FFF;
  --black:         #000;

  /* ── Fonts ── */
  --font-display:  'Big Shoulders Display', sans-serif;   /* headlines only */
  --font-ui:       'IBM Plex Mono', monospace;            /* everything else */
  --font-btc:      'OCR-Bitcoin', monospace;              /* values and readouts only */

  /* ── Spacing ── */
  /* Scale and rule: Website-Aesthetics, Design Rules */
  --gap-5:             5px;
  --gap-10:            10px;
  --gap-15:            15px;    /* reserve */
  --gap-20:            20px;    /* the default */
  --gap-30:            30px;
  --gap-40:            40px;    /* reserve */
  --gap-60:            60px;    /* reserve */
  --gap-80:            80px;    /* reserve */
  --gap-120:           120px;   /* reserve */
  --content-start:     280px;   /* one-off: where content starts below the top of a panel */

  /* ── Component tokens ── */
  --testimonial-card:   256px;   /* testimonial card body height */
  --testimonial-header: 96px;    /* testimonial card header height */
  --header-height:      140px;   /* site header height */

  /* ── Z-index scale — establishes the layering order across the site ── */
  --z-content:    1;     /* panel-content, .btn, .acc-body — normal in-flow content */
  --z-overlay:    2;     /* hover states, hover-elevated items */
  --z-active:     3;     /* .nav-item.active, .acc-item.open, .acc-header */
  --z-nav:        50;    /* side nav strip — sits below header/footer */
  --z-header:     90;    /* fixed top header */
  --z-footer:     90;    /* fixed bottom footer (same tier as header) */
  --z-cta:        100;   /* CTA boosts above page chrome (Shop Now, mobile bottom nav) */
  --z-modal:      1000;  /* reserved for future modals, dialogs, dropdowns */

}


/* ============================================================
   3. RESET
   Removes browser default spacing and sizing quirks.
   Do not edit this section.
============================================================ */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin:     0;
  padding:    0;
}




/* ============================================================
   4. BASE
   Sets up the page background, font, and scrollbar.
   Edit font-size or line-height here if needed.
============================================================ */

html {
  scroll-behavior:    smooth;
  scroll-padding-top: var(--header-height);   /* anchor jumps clear the fixed header */
}

html,
body {
  width:            100%;
  background-color: var(--bg);
  font-family:      var(--font-ui);
  color:            var(--text-body);
}

/* ── Global transitions — all interactive elements ── */
a,
button,
[role="button"],
input,
select,
textarea {
  transition-property:        background-color, color, border-color, box-shadow;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

/* ── Reduced motion — honour the visitor's system setting ──
   Switches off smooth scrolling and every transition on the site
   (header slide, mini-cart, accordion, hover colors, testimonial
   fade). The one !important in this file: it has to beat every
   transition rule below at once, and a per-selector list would
   drift out of date the first time a rule is added. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration:  0.01ms !important;
  }
}

/* ── Scrollbar — thin dark style ── */
::-webkit-scrollbar       { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--bg); }

::-webkit-scrollbar-thumb {
  background:    var(--border);
  border-radius: 0;
}

::-webkit-scrollbar-thumb:hover { background: var(--text-label); }

* {
  scrollbar-width: auto;
  scrollbar-color: var(--border) var(--bg);
}

/* ── Text selection — brand blue background, page bg as fg for contrast.
   ::-moz-selection must be in its own rule (not a comma-grouped selector)
   because Firefox drops the entire rule if any selector is unrecognized. */
::selection {
  background-color: var(--blue);
  color:            var(--bg);
}
::-moz-selection {
  background-color: var(--blue);
  color:            var(--bg);
}

/* ── Screen-reader-only utility ──
   Visually hidden but still announced to assistive tech. Used for page
   titles and labels that shouldn't render visually but must exist in DOM. */
.sr-only {
  position:    absolute;
  width:       1px;
  height:      1px;
  padding:     0;
  margin:      -1px;
  overflow:    hidden;
  clip:        rect(0, 0, 0, 0);
  white-space: nowrap;
  border:      0;
}



/* ============================================================
   5. LAYOUT
   Full-height page panels over a single fixed background.

   HOW TO USE:
   <body>
     <div class="page-bg" role="img" aria-label="Page background"></div>

     <section class="panel" id="panel-01">
       <div class="panel-content">
         <div class="panel-label">// 01 — Section Title</div>
         your content here
       </div>
     </section>
   </body>

   The .page-bg goes ONCE per page (just inside <body>) — it stays
   fixed behind every panel as the user scrolls.

   Each panel:
     - is full viewport height
     - has a 1px black divider on its top edge
     - is transparent — the .page-bg shows through
     - even-numbered panels (2, 4, 6) get a +20% black overlay (zebra rhythm)
     - centers a 1080px content column
     - uses --gap-20 between child elements inside .panel-content

   Panel padding — single source of truth on .panel-content:
     --content-start (280px) top and bottom, --gap-20 sides, at
     every viewport. No responsive overrides, no flex centering.
     280px > --header-height (140px since 12 September 2026), so content clears the fixed
     header everywhere. .panel itself carries no padding, so the two
     never stack. Every page inherits this. A page must not carry its
     own copy; the copies still sitting in the unbuilt page folders
     go when the template is fanned out.
============================================================ */

/* ── Full-height section panel ── */
.panel {
  position:          relative;
  width:             100%;
  min-height:        100vh;
  min-height:        100dvh;
  display:           flex;
  align-items:       flex-start;
  justify-content:   center;
  border-top:        solid 1px var(--black);
  scroll-margin-top: calc(var(--header-height) * -1);
}

/* ── Single fixed background — sits behind ALL panels (one per page) ── */
.page-bg {
  position:            fixed;
  inset:               0;
  z-index:             0;
  background-color:    var(--bg);
  background-image:    linear-gradient(var(--overlay-50) 0%, var(--overlay-20) 100%),
                       var(--bg-image);
  background-size:     cover;
  background-position: center;
}

/* ── Zebra rhythm — even panels get an additional 20% overlay ── */
.panel:nth-of-type(even)::before {
  content:          "";
  position:         absolute;
  inset:            0;
  background-color: var(--overlay-20);
  z-index:          0;
  pointer-events:   none;
}

/* ── CRT effect — odd panels get a terminal-screen overlay
      (scanlines + cool-black vignette). Sibling to the zebra rule above:
      even panels = workshop wash, odd panels = behind-the-glass CRT. ── */
.panel:nth-of-type(odd) {
  overflow: hidden;
}

.panel:nth-of-type(odd)::before {
  content:          "";
  position:         absolute;
  inset:            0;
  pointer-events:   none;
  z-index:          1;
  /* Pinned to the screen (background variations page, 6 September 2026):
     the page slides under the lines instead of carrying them, which
     removes the shimmer while scrolling. */
  background-attachment: fixed;
  background-image: repeating-linear-gradient(
                      to bottom,
                      transparent 0 2px,
                      rgba(245, 243, 239, 0.07) 2px 3px
                    );
}

/* Vignette sits behind the content (z 0) since 6 September 2026, so titles
   and their glows keep full strength near the panel edges. The scanlines
   above stay on top of the text. */
.panel:nth-of-type(odd)::after {
  content:          "";
  position:         absolute;
  inset:            0;
  pointer-events:   none;
  z-index:          0;
  background:       radial-gradient(
                      ellipse at center,
                      transparent 35%,
                      rgba(0, 0, 0, 0.55) 100%
                    );
}

/* ── Content sits above background and zebra overlay ── */
.panel-content {
  position:       relative;
  z-index:        var(--z-content);
  width:          100%;
  max-width:      1080px;
  padding:        var(--content-start) var(--gap-20);
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-20);
}

/* ── Section label — small uppercase marker like "// 01 — Hero" ── */
.panel-label {
  font-family:    var(--font-ui);
  font-size:      12px;
  font-weight:    400;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color:          var(--text-body);
}




/* ============================================================
   6. NAV
   Fixed side navigation — vertical strip of numbered links
   on the right edge of the screen.

   HOW TO USE:
   <nav class="nav-side" aria-label="Section navigation">
     <a class="nav-item active" href="#panel-01" aria-label="Section 01">01</a>
     <a class="nav-item" href="#panel-02" aria-label="Section 02">02</a>
     <a class="nav-item" href="#panel-03" aria-label="Section 03">03</a>
     <a class="nav-item nav-sep" href="#footer" aria-label="Jump to footer">//</a>
   </nav>

   JS auto-adds/removes the "active" class as user scrolls.
   nav-sep is excluded from active state (it's a meta jump link, not
   a section), but is fully clickable and inherits hover styling.
============================================================ */

.nav-side {
  position:       fixed;
  right:          var(--gap-20);
  top:            50%;
  transform:      translateY(-50%);
  z-index:        var(--z-nav);
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-5);
}

.nav-item {
  position:        relative;
  z-index:         0;
  display:         flex;
  align-items:     center;
  justify-content: center;
  width:           40px;
  height:          40px;
  border:          1px solid var(--border);
  background:      transparent;
  color:           var(--text-label);
  font-family:     var(--font-btc);
  font-size:       14px;
  font-weight:     400;
  letter-spacing:  0.06em;
  text-decoration: none;
  cursor:          pointer;
  transition:      color 0.5s ease, border-color 0.5s ease, background 0.5s ease, box-shadow 0.5s ease;
}


.nav-item:hover {
  z-index:      var(--z-overlay);
  color:        var(--blue);
  border-color: var(--blue);
  background:   var(--hover);
  box-shadow:   var(--shadow);
}

.nav-item.active {
  z-index:      var(--z-active);
  color:        var(--orange);
  border-color: var(--orange);
  background:   var(--active-orange);
  box-shadow:   0 0 12px 3px rgba(227, 121, 27, 0.2);   /* quiet color glow, section 7 */
}

/* ── Meta item — set off from the numbered list by one more gap,
   but otherwise styled identically to a regular .nav-item. Used for
   the "//" jump-to-footer link. Inherits .nav-item rest color and
   hover state. The margin is on both classes on purpose: .nav-item
   :hover is two classes and would overrule a one-class margin the
   moment the pointer landed on the //, taking that height out of
   the strip — and the strip is centred, so every item moved. Seen,
   measured and fixed on the variations page, 7 September 2026. */
.nav-item.nav-sep {
  margin-top: var(--gap-5);
}

/* ── Parked at the footer ──
   The footer sits above the strip, so at the bottom of a page the
   strip used to be swallowed. Instead it stops being centred and its
   bottom edge sits on the footer's top line, the same line the back
   to top tab hangs from. script.js measures the page and puts the top
   the strip should sit at in --nav-park; nothing moves at the instant
   it parks, because there the parked top and the centred top are the
   same number. Locked 13 September 2026, option E6; the reasoning is
   in Website-Aesthetics. */
.nav-side.is-parked {
  top:       var(--nav-park);
  transform: none;
}

/* Parked means the visitor has arrived at the footer, so the item that
   points at the footer is the current one and takes the current
   orange. One orange at a time: the section number hands it back and
   sits at rest until the strip un-parks. The half-second fade is on
   .nav-item already, so the orange crosses rather than blinks. */
.nav-side.is-parked .nav-sep {
  color:        var(--orange);
  border-color: var(--orange);
  background:   var(--active-orange);
  box-shadow:   0 0 12px 3px rgba(227, 121, 27, 0.2);   /* quiet color glow, section 7 */
}

.nav-side.is-parked .nav-item.active:not(.nav-sep) {
  z-index:      0;
  color:        var(--text-label);
  border-color: var(--border);
  background:   transparent;
  box-shadow:   none;
}

/* Hover is blue, everywhere, parked or not. Both rules above are
   deeper than .nav-item:hover, so each needs its own hover line or the
   parked strip would swallow the blue. */
.nav-side.is-parked .nav-sep:hover,
.nav-side.is-parked .nav-item.active:not(.nav-sep):hover {
  z-index:      var(--z-overlay);
  color:        var(--blue);
  border-color: var(--blue);
  background:   var(--hover);
  box-shadow:   var(--shadow);
}




/* ============================================================
   6.5 HEADER & FOOTER
   Sticky top header (logo · nav · cart) that hides on scroll
   down and reveals on scroll up. Bottom footer in two halves,
   the reading on the left and the buying on the right, with a
   ledger, link columns, a legal row and the socials.
   Both share an orange "horizon line" glow on their inner edge.

   HOW TO USE — HEADER (as in index.html, the master):
   <header class="header" id="site-header">
     <div class="header-inner">
       <a class="header-logo" href="/" aria-label="OMG21 home">
         <svg class="logo logo-h-48">
           <use href="/assets/logo/omg21-logos.svg#lockup-full"/>
         </svg>
       </a>
       <nav class="header-nav" aria-label="Site navigation">
         <a class="header-link" href="/">Home</a>
         <a class="header-link" href="/shop/">Shop</a>
         ...
       </nav>
       <button class="header-menu-trigger" type="button" id="menu-trigger"
               aria-expanded="false" aria-controls="header-menu"
               aria-label="Open menu">   ...hamburger icon...   </button>
       <div class="header-cart-wrap">
         <button class="header-cart" type="button" id="cart-trigger"
                 aria-expanded="false" aria-controls="mini-cart"
                 aria-label="Cart, empty">
           <span class="header-cart-total">$0.00</span>
         </button>
       </div>
     </div>
   </header>

   The cart button is the total in a green box, no icon and no
   count (mini-cart, locked 6 September 2026). script.js writes the
   total, marks the current page's link with .active, toggles
   .hidden on .header as the user scrolls, and opens the mini-cart
   and the mobile menu from the two buttons. Nothing is inline.

   HOW TO USE — FOOTER (locked 11 September 2026, option 59 on
   .dev/01-background/footer.html):
   <footer class="footer">
     <a class="footer-to-top" href="#01">   ...tab on the centre line... </a>
     <div class="footer-inner">
       <div class="footer-main">                         two equal halves, a rule between
         <div class="footer-half footer-half-links">
           <div class="footer-signoff">
             <p class="footer-wordmark text-display3-60px"> ...the name... </p>
           </div>
           <div class="footer-cols">
             <div class="footer-col"> ...Index... </div>
             <div class="footer-col"> ...Support... </div>
             <div class="footer-col"> ...Proof-of-Work... </div>
           </div>
         </div>
         <div class="footer-half footer-half-shop">
           <div class="footer-ledger">                  block height, then the two prices
             <div class="footer-ledger-row footer-ledger-block"> ...label, leader, value... </div>
             <div class="footer-ledger-row">                     ...label, leader, value... </div>
             <div class="footer-ledger-row">                     ...label, leader, value... </div>
           </div>
           <div class="footer-actions">
             <div class="footer-cart">
               <span class="footer-cart-total">$0.00</span>   (a readout, not a link)
               <a class="footer-cart-label" href="/cart/">View Cart</a>
             </div>
             <a class="footer-checkout" href="/checkout/">Checkout</a>
           </div>
         </div>
       </div>
       <div class="footer-bottom">                       legal left, socials centre, block right
         <div class="footer-legal">      ...privacy/terms/refund... </div>
         <div class="footer-social">     ...four icons, on the centre line... </div>
         <div class="footer-foundation"> ...block credit... </div>
       </div>
     </div>
   </footer>
============================================================ */


/* ── Header — fixed, hides on scroll down ── */
.header {
  position:            fixed;
  top:                 0;
  left:                0;
  width:               100%;
  height:              var(--header-height);
  z-index:             var(--z-header);
  background-color:    #050403;        /* solid ground, one step up from black on the page ground's hue: G2 on the footer page, 11 September 2026. Same value as the footer. */
  border-bottom:       1px solid var(--text-heading);
  box-shadow:          0 -70px 70px 50px var(--orange);
  transform:           translateY(0);
  transition:          transform 0.3s ease;
}

/* Strong dark edges (B4). Sits at z -1 inside the header's own stacking
   context: above the ground, below the logo, links and cart. */
.header::after {
  content:        "";
  position:       absolute;
  inset:          0;
  z-index:        -1;
  pointer-events: none;
  background:     radial-gradient(ellipse at center, transparent 35%, var(--overlay-70) 100%);
}

.header.hidden { transform: translateY(-100%); }

/* ── Side-nav anchor jump → chrome-less section view ──
   Body class is added by script.js's SIDE-NAV ANCHOR JUMP block on
   .nav-item click, removed on the first user-initiated scroll input
   (wheel / touchmove / scroll keys). Specificity (0,2,1) beats
   .header.hidden (0,2,0), so a scroll-tick that would normally
   remove .hidden at y<=0 can't expose the header mid-jump. The
   companion structural fix in script.js's scroll handler skips the
   auto-show while this class is on the body — without it, a 'scroll'
   event would race and need !important here. */
body.nav-anchor-jumping .header { transform: translateY(-100%); }

/* ── Inner wrapper — caps chrome at 1200px, centered. The content
   container (.panel-content / .test-content) stays at 1080px — chrome
   is intentionally 120px wider than content to frame it. ── */
.header-inner {
  max-width:       1200px;
  height:          100%;
  margin:          0 auto;
  padding:         0 var(--gap-20);
  display:         flex;
  align-items:     center;
  justify-content: space-between;
}

.header-logo {
  display:         flex;
  align-items:     center;
  text-decoration: none;
}

.header-nav {
  display: flex;
  gap:     var(--gap-20);
}

.header-link {
  font-family:     var(--font-ui);
  font-size:       21px;
  font-weight:     400;
  letter-spacing:  0.18em;
  text-transform:  uppercase;
  color:           var(--text-label);
  text-decoration: none;
  padding:         var(--gap-10) var(--gap-5);
}

.header-link:hover  { color: var(--blue);   }
.header-link.active { color: var(--orange); }

/* ── Cart (right) ──
   Toggle button — opens the mini-cart popover. Orange-only accent
   across rest / hover / open ("one accent per component"). Blue is
   reserved for hover elsewhere; not here. */
.header-cart-wrap { position: relative; }

.header-cart {
  display:          flex;
  align-items:      center;
  gap:              var(--gap-20);
  padding:          var(--gap-10) var(--gap-30);
  background-color: var(--rest);
  border:           1px solid var(--green);
  font-family:      var(--font-btc);
  font-size:        21px;
  font-weight:      400;
  text-transform:   uppercase;
  color:            var(--text-label);
  text-decoration:  none;
  cursor:           pointer;
}

.header-cart:hover {
  border-color: var(--green);
  color:        var(--orange);
  background-color: var(--hover);
  box-shadow:   0 0 12px 3px rgba(50, 180, 67, 0.2);   /* quiet color glow, section 7 */
}

/* Open state — matches "active nav / open accordion" pattern */
.header-cart[aria-expanded="true"] {
  border-color:     var(--green);
  background-color: var(--active-green);
  color:            var(--orange);
  box-shadow:       0 0 12px 3px rgba(50, 180, 67, 0.2);   /* quiet color glow, section 7 */
}

/* Total readout — neutral pre-payment (green is reserved for paid).
   Span isn't covered by the global transition selector — match its 0.5s ease.

   The width is held, not grown. The header is laid out end to end, so
   a total that got wider used to push the nav sideways as the cart
   filled. The face is monospace, so ten characters is an exact
   measure: $99,999.99, five digits and the cents, the widest total the
   shop can reach. Under that the figure is centred in the same box and
   nothing on the header moves (owner, 13 September 2026). */
.header-cart-total {
  min-width:                  10ch;
  text-align:                 center;
  color:                      var(--green);
  transition-property:        color;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

.header-cart:hover .header-cart-total,
.header-cart[aria-expanded="true"] .header-cart-total { color: var(--green); }

/* ── Mobile menu (hamburger trigger + fullscreen overlay) ──
   Hidden on desktop, shown ≤768px. Mutual-exclusion with mini-cart
   handled in script.js (one open at a time). */

.header-menu-trigger {
  display:          none;
  align-items:      center;
  justify-content:  center;
  padding:          var(--gap-10);
  background-color: var(--rest);
  border:           1px solid var(--border);
  cursor:           pointer;
  color:            var(--text-label);
}

.header-menu-trigger:hover {
  border-color: var(--orange);
  color:        var(--orange);
}

.header-menu-trigger[aria-expanded="true"] {
  background-color: var(--active-orange);
  border-color:     var(--orange);
  color:            var(--orange);
}

.header-menu-icon {
  color:   var(--orange);
  width:   24px;
  height:  24px;
  display: block;
}

/* Fullscreen overlay — z-index 80 sits below z-header (90) so the
   trigger stays tappable while the overlay is visible. */
.header-menu {
  position:            fixed;
  top:                 0;
  left:                0;
  width:               100%;
  height:              100vh;
  height:              100dvh;
  padding:             calc(var(--header-height) + var(--gap-20)) var(--gap-20) var(--gap-20);
  background-color:    var(--bg);
  background-image:    linear-gradient(var(--overlay-70) 0%, var(--overlay-70) 100%),
                       var(--bg-image);
  background-position: center center;
  background-size:     cover;
  display:             flex;
  flex-direction:      column;
  gap:                 var(--gap-20);
  overflow-y:          auto;
  overscroll-behavior: contain;
  z-index:             80;
}

.header-menu[hidden] { display: none; }

/* Close button — shared by menu overlay and mini-cart panel.
   Text-only "Close", neutral at rest, red on hover/active.
   Mirrors .btn-standard-red recipe (~line 1472). */
.header-menu-close {
  align-self:       flex-end;
  display:          flex;
  align-items:      center;
  justify-content:  center;
  padding:          var(--gap-10);
  background-color: transparent;
  border:           1px solid var(--border);
  border-radius:    0;
  color:            var(--border);
  font-family:      var(--font-ui);
  font-size:        14px;
  letter-spacing:   0.18em;
  text-transform:   uppercase;
  line-height:      1;
  cursor:           pointer;
  flex-shrink:      0;
}

.header-menu-close:hover {
  background-color: var(--hover);
  border-color:     var(--red);
  color:            var(--red);
  box-shadow:       var(--shadow);
}

.header-menu-close:active {
  background-color: var(--active-red);
  border-color:     var(--red);
  color:            var(--red);
  box-shadow:       var(--shadow);
}

/* Stacked nav links inside the menu overlay */
.header-menu-nav {
  display:        flex;
  flex-direction: column;
  gap:            0;
}

.header-menu-link {
  font-family:     var(--font-ui);
  font-size:       18px;
  font-weight:     400;
  letter-spacing:  0.18em;
  text-transform:  uppercase;
  color:           var(--text-heading);
  text-decoration: none;
  padding:         var(--gap-20) 0;
  border-bottom:   1px solid var(--border);
}

.header-menu-link:first-child { border-top: 1px solid var(--border); }

.header-menu-link:hover  { color: var(--orange); }
.header-menu-link.active { color: var(--orange); }

/* ── Mini-cart popover ──
   Anchored to .header-cart-wrap; toggled via [hidden] from /script.js.
   Orange-only accent: open trigger + Checkout. View Cart is secondary
   neutral. Money — line totals and the subtotal — is green in the readout
   font, matching the header's cart total (owner, 6 September 2026; this
   replaced an earlier rule keeping green for paid/confirmed only). */

.mini-cart {
  position:            fixed;
  top:                 calc(var(--header-height) + var(--gap-20));
  /* 100%, not 100vw: 100vw counts the scrollbar and the header does not,
     so the panel sat a scrollbar's width left of the header's cart and
     the footer's edge. A fixed element's 100% is the screen without the
     scrollbar, which is the same width the header is laid out in. */
  right:               max(var(--gap-20), calc((100% - 1200px) / 2 + var(--gap-20)));
  width:               560px;
  padding:             var(--gap-20);
  /* Black with the buttons' own 3% cream laid over it. Every hover on the
     site is a black wash and a black glow, so on plain black there was
     nothing to see; this lifts the ground just enough for them to read
     (mini-cart page round seven, 7 September 2026). */
  background-color:    var(--black);
  background-image:    linear-gradient(var(--rest), var(--rest));
  border:              1px solid var(--border);
  box-shadow:          var(--shadow);
  display:             flex;
  flex-direction:      column;
  gap:                 var(--gap-20);
  /* min-height gives the empty state real vertical room so .mini-cart-empty's
     flex:1 + flex centering can pin "CART EMPTY" to the panel's middle.
     Populated cart grows past this naturally — only affects empty state. */
  min-height:          280px;
  z-index:             var(--z-modal);
  transition:          top 0.3s ease;
}

/* When header hides on scroll-down, cart slides up to sit 20px from the
   viewport top — matching the header's 0.3s ease so they move in sync.
   Sibling selector works because .mini-cart is a direct child of <body>. */
.header.hidden ~ .mini-cart { top: var(--gap-20); }

.mini-cart[hidden] { display: none; }

/* Empty state — centered message, no items / subtotal / actions.
   Absolute inset:0 fills the panel (ignoring its asymmetric mobile padding
   that pushes content below the header) and flex centering pins "CART EMPTY"
   to the panel's true visual middle. pointer-events:none so anything under
   it stays clickable. Hidden via [hidden] when the cart populates — no
   impact on the items view. */
.mini-cart .mini-cart-empty {
  position:        absolute;
  inset:           0;
  display:         flex;
  align-items:     center;
  justify-content: center;
  text-align:      center;
  margin:          0;
  padding:         var(--gap-20);
  pointer-events:  none;
  color:           var(--text-label);
}

/* Populated wrapper re-establishes vertical rhythm inside the panel */
.mini-cart-body {
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-20);
}

/* Line items — injected dynamically from cart + catalog */
/* Scrolls once the cart is long (mini-cart page picks, 6 September 2026):
   the items move, the subtotal and the two buttons stay put, so Checkout
   is always reachable. padding-right keeps the line money and the item's
   hover outline clear of the scrollbar. overscroll-behavior stops the
   page behind from scrolling at the ends. */
.mini-cart-list {
  list-style:          none;
  display:             flex;
  flex-direction:      column;
  gap:                 var(--gap-10);
  max-height:          420px;
  overflow-y:          auto;
  overscroll-behavior: contain;
}

/* Author display:flex above outranks the UA [hidden]{display:none} rule
   (same specificity, later wins), so the [hidden] attribute set by JS
   wouldn't actually hide these. Bump specificity with attribute selectors
   so `bodyEl.setAttribute('hidden','')` correctly collapses them. The
   .mini-cart prefix on the empty rule beats .mini-cart .mini-cart-empty
   at 0,3,0 > 0,2,0. */
.mini-cart .mini-cart-empty[hidden],
.mini-cart-body[hidden],
.mini-cart-list[hidden] { display: none; }

/* Two rows (mini-cart page rounds four and five, 6 September 2026). Top:
   the link — picture, name, variant — across the whole width, so a long
   name has the panel to spell out in. Bottom: the quantity stepper under
   the picture on the left, the line's money on the right. */
.mini-cart-item {
  display:               grid;
  grid-template-columns: auto 1fr;
  grid-template-rows:    auto auto;
  gap:                   var(--gap-5) var(--gap-10);
  align-items:           start;
  /* The air from the scrollbar sits inside the item, not on the list
     (owner, 16 September 2026). On the list it shortened every item's
     separator by 10 while the subtotal's rule below ran full width, so
     the two lines did not end together. Padding is inside the border
     box, so the separator now runs the list's full width and the money
     still clears the bar. */
  padding-right:         var(--gap-10);
}

/* Hairline divider between items — same --border token as .mini-cart-divider
   so the rhythm stays consistent. :not(:last-child) keeps the last row from
   stacking a divider against the existing one above the subtotal block. */
.mini-cart-item:not(:last-child) {
  border-bottom:  1px solid var(--border);
  /* The same air above the line as below it (owner, 16 September 2026).
     Below the line is the list's 10 plus the next link's 10 of padding
     plus its 1px of outline room, 21; above is this padding alone, so it
     is the same 21 — 20 plus the outline pixel, as the stepper's 11 is
     10 plus it. Placement against the outline, not spacing. */
  padding-bottom: 21px;
}

/* Link wraps thumb + meta — clickable row that navigates to product page.
   The stepper and the money are siblings so they don't get caught in the
   link click. Hover follows the side-nav recipe: full blue outline + wash
   + shadow. Transparent border at rest reserves space so the layout stays
   stable. The picture is as wide as the stepper under it, 130: 30 + 5 +
   60 + 5 + 30. Every part of that is a scale step, but the total is a
   sum and so is not itself on the scale — kept because the two edges
   lining up is what the owner is reading (owner, 16 September 2026).
   The name sits level with the picture's top edge. */
.mini-cart-link {
  grid-column:           1 / -1;
  grid-row:              1;
  display:               grid;
  grid-template-columns: 130px 1fr;    /* must match .mini-cart-thumb size */
  gap:                   var(--gap-20);
  align-items:           start;
  padding:               var(--gap-10);
  border:                1px solid transparent;
  text-decoration:       none;
  color:                 inherit;
  cursor:                pointer;
}

.mini-cart-link:hover {
  border-color:     var(--blue);
  background-color: var(--rest);      /* lightens, so it reads on the panel's ground */
  color:            var(--border);
  box-shadow:       var(--shadow);
}

/* The name is a title, so it wears a title class (owner, 16 September
   2026): .text-h3-21px, the smallest of the three. It was paragraph text
   forced into capitals, which nothing else on the site does, and it read
   tight because paragraph spacing is drawn for lowercase.

   The class carries the small-title glow at rest. The mini-cart lights
   its name on hover only (round seven, 7 September), so the glow is held
   off here and the hover rule below puts it back — the locked behavior
   is unchanged, only the type. */
.mini-cart-name.text-h3-21px {
  color:       var(--text-heading);
  text-shadow: none;
}

/* The variant dims to label grey; the name stays cream and lights up with
   the small-title glow, recipe 05 — the same glow every title below
   display 1 carries (mini-cart page round seven, 7 September 2026). */
.mini-cart-link:hover .mini-cart-variant { color: var(--text-label); }

.mini-cart-link:hover .mini-cart-name {
  color: var(--text-heading);
  text-shadow:
    0 0  1px rgba(245, 243, 239, 1),
    0 0  6px rgba(227, 121,  27, 0.8),
    0 0 20px rgba(227, 121,  27, 0.35);
}

.mini-cart-thumb {
  width:      130px;
  height:     130px;
  object-fit: cover;
  display:    block;
  border:     1px solid var(--border);
}

.mini-cart-meta {
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-5);
  min-width:      0;
}

/* Spans aren't covered by the global transition selector — match the
   site's standard 0.5s ease so the hover color shift animates. */
.mini-cart-name,
.mini-cart-variant,
.mini-cart-price,
.mini-cart-sats {
  transition-property:        color, text-shadow;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}
/* No letter-spacing here: the 0.06em this carried never applied. It and
   .text-body-18px weigh the same and the type class sits later in the
   file, so the name has always drawn at body's 0 (measured 16 September
   2026). Removed rather than made to work — 18 takes 0 as body copy. */
.mini-cart-name {
  color:          var(--text-heading);
  text-transform: uppercase;
}
.mini-cart-variant {
  font-family:    var(--font-ui);
  font-size:      12px;
  letter-spacing: 0.14em;   /* 12's own tracking, the type lock of 14 September */
  text-transform: uppercase;
  color:          var(--text-label);
}
/* Money on a line and in the subtotal: the readout font and green, same
   as the header's cart total (owner, 6 September 2026). The line shows
   what that line costs — quantity × unit — not the price of one; the
   quantity is on the stepper. On the line the money is one row: dollars
   and sats the same size, 14, split by an orange slash — at the right
   edge of the stepper's row, on the stepper's bottom line. Colour tells
   them apart, not size: green dollars, cream sats. The subtotal runs the
   same row one step up the scale, at 21, which is the only thing that
   marks it as the total (owner, 7 September 2026).
   The row may wrap. On a 390 phone a line of 21 — $1,049.79 with a
   seven-figure sats total — is 233 wide and has 205 of room, so the sats
   drop to their own line under the dollars, right-aligned, and the money
   ends on the same edge as every other line. Wherever the row fits, and
   it does at 768 and 1440, nothing moves (12 September 2026). */
.mini-cart-total {
  grid-column:     2;
  grid-row:        2;
  align-self:      end;
  justify-self:    end;
  display:         flex;
  flex-wrap:       wrap;
  justify-content: flex-end;
  align-items:     baseline;
  gap:             var(--gap-10);
  row-gap:         0;      /* the wrapped sats sit straight under the dollars, no extra air */
}

/* The slash between dollars and sats. Set in Plex: the readout font
   holds only plain keyboard characters and a slash is one, but Plex
   keeps it the same weight as the labels around it. Orange, the one
   accent on the line. */
.mini-cart-sep {
  font-family:    var(--font-ui);
  font-size:      14px;
  letter-spacing: 0.12em;   /* 14's own tracking, the type lock of 14 September */
  color:          var(--orange);
}

.mini-cart-price,
.mini-cart-subtotal-value {
  font-family:    var(--font-btc);
  font-size:      14px;
  letter-spacing: 0.12em;   /* 14's own tracking, as .text-data-14px carries */
  color:          var(--green);
}

/* Trap: the markup also puts .text-copy-16px on the subtotal value, and
   that token comes later in this file at equal weight, so it wins on
   source order. This compound keeps the size and the green. Same shape
   as the .mini-cart-name fix above. (The font no longer needs the bump:
   .text-copy-16px has been OCR-Bitcoin since 14 September 2026.) */
.mini-cart-subtotal-value.text-copy-16px {
  font-size:      14px;      /* .text-copy-16px carries 16 */
  letter-spacing: 0.12em;    /* and 16's 0.10em with it; 14 takes 0.12em */
  color:          var(--green);
}

/* In the subtotal the sats go up a step, to 21, and the dollars stay at
   the lines' 14. The sats are the figure that matters here — the total in
   the money the site is actually priced in — so it is the one that grows
   (owner, 7 September 2026). Dollars and slash are unchanged. */
.mini-cart-subtotal .mini-cart-sats {
  font-size:      21px;
  letter-spacing: 0.06em;   /* 21's own tracking: the size changes, so the tracking does too */
}

/* Sats alongside the dollars, neutral. Hidden until the live rate lands,
   so a failed fetch leaves dollars on their own rather than a gap. */
.mini-cart-sats {
  font-family:    var(--font-btc);
  font-size:      14px;
  letter-spacing: 0.12em;   /* 14's own tracking, as .text-data-14px carries */
  color:          var(--text-body);
}

.mini-cart-sats[hidden] { display: none; }

/* ── Quantity stepper ──
   Minus, a field you can type in, plus. Under the picture.
   Every press or typed number changes the cart on the spot — there is no
   Update button because there is nothing to submit. Zero removes the
   line, reached by minus or by typing it; an empty field goes back to
   what it was; over 21 becomes 21 (the cart's ceiling). There is no
   remove control: zero is the way out. The two strokes carry their own
   meaning: the minus is red because it takes away and, at one, removes
   the line; the plus is green because it adds. Each hovers into its own
   colour rather than the site's blue — the exception that earns itself,
   since these are the only two buttons whose jobs are opposites
   (mini-cart page round seven, 7 September 2026). The field is a 3%
   cream wash inside a form-grey line, the number in label grey; clicking
   it turns that line blue, and the strokes do not change, because they
   did not. Every state fades at the site's 0.5s ease, set by the global
   transition rule near the top of this file. */
.mini-cart-stepper {
  grid-column:  1;
  grid-row:     2;
  display:      flex;
  align-items:  center;
  gap:          var(--gap-5);
  padding-left: 11px;      /* the link's 10px padding + 1px border: flush with the picture */
}

.mini-cart-stepper-button {
  position:         relative;
  width:            30px;
  height:           30px;
  padding-top:      3px;   /* the OCR-Bitcoin nudge, see .mini-cart-stepper-value */
  display:          flex;
  align-items:      center;
  justify-content:  center;
  background-color: transparent;
  border:           1px solid var(--border);
  border-radius:    0;
  font-family:      var(--font-btc);   /* typed characters, since they sit beside a number */
  font-size:        18px;              /* one step under the locked field's 21 */
  line-height:      1;
  cursor:           pointer;
}

.mini-cart-stepper-button.is-minus { color: var(--red); }
.mini-cart-stepper-button.is-plus  { color: var(--green); }

/* The line comes up to the button's own color with the locked quiet glow
   behind it, the move every color-at-rest button makes (15 September 2026) */
.mini-cart-stepper-button.is-minus:hover:not([disabled]),
.mini-cart-stepper-button.is-minus:active:not([disabled]) {
  background-color: var(--active-red);
  border-color:     var(--red);
  box-shadow:       0 0 12px 3px rgba(224, 48, 48, 0.2);
}

.mini-cart-stepper-button.is-plus:hover:not([disabled]),
.mini-cart-stepper-button.is-plus:active:not([disabled]) {
  background-color: var(--active-green);
  border-color:     var(--green);
  box-shadow:       0 0 12px 3px rgba(50, 180, 67, 0.2);
}

/* At the ceiling the plus goes quiet */
/* At the ceiling the plus goes quiet and the pointer says so. No
   pointer-events:none — that stops the cursor rule reaching it, and a
   disabled button never fires a click anyway (owner, 16 September 2026). */
.mini-cart-stepper-button[disabled] {
  color:        var(--disabled);
  border-color: var(--disabled);
  cursor:       not-allowed;
}

.mini-cart-stepper-value {
  width:            60px;
  height:           30px;
  min-width:        0;
  /* OCR-Bitcoin's ink sits above the middle of a box that centers it:
     1.5px at 18 and 21, 1px at 14 (measured 16 September 2026; the
     font's ascent outweighs its descent). Twice that in padding-top
     drops the ink to center, and border-box keeps the box its size.
     Every box that centers this font takes the nudge. */
  padding:          2px 0 0;
  /* One line color across all three boxes (owner, 16 September 2026).
     The field used to rest in --placeholder while the buttons rested in
     --border, and the two grays read as a mistake at this size. */
  border:           1px solid var(--border);
  background-color: var(--rest);
  text-align:       center;
  font-family:      var(--font-btc);
  font-size:        14px;
  letter-spacing:   0.12em;   /* 14's own tracking, the type lock of 14 September */
  /* Tracking adds its space after the last character too, so centered
     text sits half a space left of true center. The indent puts it back. */
  text-indent:      0.12em;
  color:            var(--text-label);
  appearance:       textfield;   /* the standard property, with the Firefox prefix below */
  -moz-appearance:  textfield;
}

.mini-cart-stepper-value:hover { border-color: var(--blue); }

/* Clicked in: the blue line and the locked quiet glow in blue */
.mini-cart-stepper-value:focus {
  outline:      none;
  border-color: var(--blue);
  box-shadow:   0 0 12px 3px rgba(47, 53, 224, 0.2);
}

/* Caution, at the 21 ceiling: the field's line and number go yellow, the
   strokes do not change. `script.js` puts .is-caution on the stepper.
   Last so it holds over hover and the clicked-in blue — a field keeps
   its own color through every state, as the locked component does. */
.mini-cart-stepper.is-caution .mini-cart-stepper-value {
  border-color: var(--yellow);
  color:        var(--yellow);
}

.mini-cart-stepper.is-caution .mini-cart-stepper-value:focus {
  border-color:     var(--yellow);
  background-color: var(--active-yellow);
  box-shadow:       0 0 12px 3px rgba(216, 208, 22, 0.2);
}

.mini-cart-divider {
  height:           1px;
  border:           none;
  background-color: var(--border);
  margin:           0;
}

/* Subtotal: the label on its own line above, then the money on one line
   under it — the same shape as a line item's money, so the eye reads the
   column straight down (mini-cart page round five, 6 September 2026).
   Everything right-aligned; padding-right of 20 puts its right edge near
   the line money's, which clears the scrollbar by the list's own 10. */
.mini-cart-subtotal {
  display:         flex;
  flex-wrap:       wrap;
  justify-content: flex-end;
  align-items:     baseline;
  gap:             var(--gap-10);
  padding-right:   var(--gap-20);
  /* Room under the rule and before View Cart, on top of the panel's own
     20 (owner, 7 September 2026). */
  padding-top:     var(--gap-10);
  padding-bottom:  var(--gap-10);
}

.mini-cart-subtotal .text-label-14px {
  flex-basis: 100%;      /* takes the whole first line, so the money drops below */
  text-align: right;
}

/* Action buttons inside the panel keep the standard .btn size — 60px
   tall, Plex uppercase 21px (Website-Aesthetics). An earlier 48px/16px
   override was removed 6 September 2026: the buttons are the same
   buttons wherever they appear. */


/* ── Footer ── */

.footer {
  position:            relative;
  width:               100%;
  background-color:    #050403;        /* solid ground, one step up from black on the page ground's hue: G2 on the footer page, 11 September 2026. Same value as the header. */
  border-top:          1px solid var(--text-heading);
  box-shadow:          0 70px 70px 50px var(--orange);
  z-index:             var(--z-cta);
}

/* Dark edges, left and right sides only (E5). Sits at z -1 inside the
   footer's own stacking context: above the ground, below the tiles. */
.footer::after {
  content:        "";
  position:       absolute;
  inset:          0;
  z-index:        -1;
  pointer-events: none;
  background:     linear-gradient(to right, rgba(0, 0, 0, 0.55) 0%, transparent 25%, transparent 75%, rgba(0, 0, 0, 0.55) 100%);
}

/* Two halves on one centre line: reading on the left (the name, the
   three link columns, the socials), buying on the right (the ledger,
   the cart line, Checkout). Locked 11 September 2026 from option 59
   on .dev/01-background/footer.html, nine rounds. The inner box is a
   column of two blocks, 60 apart: the halves, then the bottom row. */
.footer-inner {
  max-width:      1200px;
  margin:         0 auto;
  padding:        var(--gap-80) var(--gap-20) var(--gap-60);
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-60);
}

/* The halves are equal and the grid has no gap, so the rule between
   them (the left border of the right half) lands on the exact centre
   of the content, under the back-to-top, and the two read as one
   centre line. The 60 of breathing room each side of the rule is
   padding on the halves, not a gap. */
.footer-main {
  display:               grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap:                   0;
  padding-bottom:        var(--gap-30);
  border-bottom:         1px solid var(--border);
}

/* The bottom row: legal links left, the four socials on the centre
   line, Foundation Block right. Three grid columns with the outer two
   equal, so the socials sit on the exact centre whatever the legal
   run and the block credit measure, under the rule and the arrow.
   Pulled up 30 of the 60 so it sits 30 under the rule. Socials here
   since 12 September 2026, option 8 of the socials round. */
.footer-bottom {
  display:               grid;
  grid-template-columns: 1fr auto 1fr;
  align-items:           center;
  gap:                   var(--gap-30);
  margin-top:            -30px;
}

.footer-bottom .footer-legal      { justify-self: start; }
.footer-bottom .footer-foundation { justify-self: end; }

/* ── Left half ── */
.footer-half-links {
  display:        flex;
  flex-direction: column;
  padding-right:  var(--gap-60);
}

/* The name's box, the width of the half's content. The name is
   display 3 and wraps to three lines. 40 under it to the column
   titles (the owner's number, 12 September 2026): the name is its own
   block, and the auto margin takes any room beyond that, keeping the
   columns on the floor of the half. The socials lived in this box's
   corner for a day; they are in the bottom row now. */
.footer-signoff {
  margin-bottom:  auto;
  padding-bottom: var(--gap-40);
}

.footer-wordmark { margin: 0; }   /* the type class carries the face, the size and the glow */

/* ── Link columns: Index, Support, Proof-of-Work, each as wide as its
   words, 30 apart. The order mirrors the header (Shop and About lead)
   and is the clean stair, each column wider than the last. ── */
.footer-cols {
  display: flex;
  gap:     var(--gap-30);
}

.footer-col {
  display:        flex;
  flex-direction: column;
  flex:           0 0 auto;
}

.footer-col-title {
  font-family:    var(--font-ui);
  font-size:      18px;
  font-weight:    400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space:    nowrap;
  color:          var(--text-heading);
  margin-bottom:  var(--gap-20);
}

/* The // before each title, in the bold weight and orange */
.footer-col-title::before {
  content:     "// ";
  font-weight: 700;
  color:       var(--orange);
}

.footer-col-list {
  list-style:     none;
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-10);
  padding-left:   1ch;   /* branch starts ~1ch in from header bracket */
}

.footer-col-list li {
  position:     relative;
  padding-left: 4ch;     /* room for "├── " / "└── " connector */
}

/* Tree-directory connector — single rail. Stays muted regardless of link state. */
.footer-col-list li::before {
  content:      "├── ";
  position:     absolute;
  left:         0;
  font-family:  var(--font-ui);
  color:        var(--text-label);
  line-height:  inherit;
}

.footer-col-list li:last-child::before {
  content: "└── ";
}

.footer-col-link {
  font-family:     var(--font-ui);
  font-size:       16px;
  font-weight:     400;
  color:           var(--text-body);
  text-decoration: none;
}

.footer-col-link:hover                { color: var(--blue);   }
.footer-col-link[aria-current="page"] { color: var(--orange); }

/* ── Right half: the ledger at the top, the cart line and Checkout on
   the floor, a rule between them ── */
.footer-half-shop {
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-30);
  padding-left:   var(--gap-60);
  border-left:    1px solid var(--border);
}

/* The ledger: three rows of label, dot leader, value, all in the
   Bitcoin face. Block height leads at 36 under its own rule; the two
   prices follow at 21. Only the number's size sets the block apart. */
.footer-ledger {
  display:        flex;
  flex-direction: column;
}

.footer-ledger-row {
  display:     flex;
  align-items: baseline;
  gap:         var(--gap-20);
  padding:     var(--gap-5) 0;
}

.footer-ledger-block {
  padding:       0 0 var(--gap-20);
  margin-bottom: var(--gap-15);
  border-bottom: 1px solid var(--border);
}

.footer-ledger-block .footer-tile-value {
  font-size:   36px;
  line-height: 1;
}

/* The dot leader takes whatever room the label and the value leave,
   and clips its run of dots to fit */
.footer-ledger-leader {
  flex:           1 1 0;
  min-width:      20px;
  overflow:       hidden;
  white-space:    nowrap;
  font-family:    var(--font-ui);
  font-size:      12px;
  letter-spacing: 0.10em;
  line-height:    1;
  color:          var(--text-label);
}

.footer-ledger-leader::before {
  content: "................................................................................................................................";
}

/* A label on two lines beside its number reads as a fault, so it
   never wraps at any width. The leader gives up its room instead —
   it is a run of dots and has none of its own to keep. Block Height
   was breaking in two on a tablet and a phone before this. */
.footer-tile-label {
  font-family:    var(--font-btc);
  font-size:      14px;   /* 12 to 14, owner, 14 September 2026 */
  letter-spacing: 0.10em;
  text-transform: uppercase;
  white-space:    nowrap;
  color:          var(--text-label);
}

.footer-tile-value {
  font-family: var(--font-btc);
  font-size:   21px;
  color:       var(--text-heading);
  line-height: 1;
}

/* The ledger's three rows are readings, not money, so they share the
   off-white every other readout uses (owner, 20 September 2026). The
   BTC price was green until then; the only green left in this column
   is the cart total, which is the buyer's own money. */
.footer-tile-value.usd { color: var(--text-heading); }

/* The cart line and Checkout sit on the floor of the half, so they
   end level with the last link across the rule, with a rule above
   them parting them from the readouts */
.footer-actions {
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-20);
  margin-top:     auto;
  padding-top:    var(--gap-30);
  border-top:     1px solid var(--border);
}

/* The cart line: the total and View Cart side by side, two boxes
   splitting the line in half, 60 tall like the button under them.
   The total is a readout and nothing else; View Cart is the link. */
.footer-cart {
  display:     flex;
  align-items: center;
  gap:         var(--gap-20);
  height:      60px;
}

.footer-cart-total,
.footer-cart-label {
  display:          flex;
  align-items:      center;
  justify-content:  center;
  align-self:       stretch;
  flex:             1 1 0;
  border:           1px solid var(--border);
  background-color: var(--rest);
}

/* The money reads as money: the Bitcoin face in green, in a green
   box, as the header's cart total does. It is a button: pressing it
   opens the mini-cart, the same panel the header's total opens, so the
   cart is reachable from the bottom of the page without scrolling back
   up. The padding line is the browser's own button padding removed;
   everything else is the box it already was. */
.footer-cart-total {
  padding:      0;
  font-family:  var(--font-btc);
  font-size:    21px;
  font-weight:  400;
  color:        var(--green);
  border-color: var(--green);
  cursor:       pointer;
}

/* Hover matches the header's cart: the box lifts, the money stays
   green. The panel it opens hangs off the header; script.js copies the header cart's open state onto
   this button, so it glows while the cart is open, as the header's does. */
.footer-cart-total:hover {
  background-color: var(--hover);
  box-shadow:       0 0 12px 3px rgba(50, 180, 67, 0.2);   /* quiet color glow, section 7 */
}

.footer-cart-total[aria-expanded="true"] {
  background-color: var(--active-green);
  box-shadow:       0 0 12px 3px rgba(50, 180, 67, 0.2);   /* quiet color glow, section 7 */
}

/* View Cart is the standard button: grey at rest, blue on hover */
.footer-cart-label {
  font-family:     var(--font-ui);
  font-size:       21px;
  font-weight:     400;
  letter-spacing:  0.08em;
  text-transform:  uppercase;
  text-decoration: none;
  color:           var(--text-label);
}

.footer-cart-label:hover {
  border-color:     var(--blue);
  background-color: var(--hover);
  color:            var(--blue);
  box-shadow:       var(--shadow);
}

/* Checkout: the one orange button in the footer, the full width of
   the half, the last thing in it */
.footer-checkout {
  display:          flex;
  align-items:      center;
  justify-content:  center;
  flex:             none;
  height:           60px;
  border:           1px solid var(--orange);
  color:            var(--orange);
  background-color: var(--rest);
  font-family:      var(--font-ui);
  font-size:        21px;
  font-weight:      400;
  letter-spacing:   0.08em;
  text-transform:   uppercase;
  text-decoration:  none;
}

.footer-checkout:hover {
  background-color: var(--hover);
  box-shadow:       0 0 12px 3px rgba(227, 121, 27, 0.2);   /* quiet color glow, section 7 */
}

.footer-checkout:active {
  background-color: var(--active-orange);
  box-shadow:       var(--shadow);
}

/* ── Bottom row ── */

/* Legal links — Privacy / Terms / Refund, at the left end */
.footer-legal {
  display:     flex;
  align-items: center;
  gap:         var(--gap-10);
  white-space: nowrap;
}

.footer-legal-link {
  font-family:     var(--font-ui);
  font-size:       12px;
  font-weight:     300;
  color:           var(--text-label);
  text-decoration: none;
  transition:      color 0.5s ease;
}

.footer-legal-link:hover                { color: var(--blue);   }
.footer-legal-link[aria-current="page"] { color: var(--orange); }

/* Separator (//) is decoration only — dimmer than the links so
   it reads as schematic punctuation, not content. aria-hidden in HTML. */
.footer-legal-sep {
  font-family: var(--font-ui);
  font-size:   12px;
  font-weight: 300;
  color:       var(--text-label);
}

/* Socials — four bare icons, no box, 20 apart, in the middle column
   of the bottom row. Grey at rest, blue on hover. */
.footer-social {
  display: flex;
  gap:     var(--gap-20);
}

.footer-social-link {
  display:         flex;
  align-items:     center;
  justify-content: center;
  width:           24px;
  height:          24px;
  color:           var(--text-label);
}

.footer-social-link:hover { color: var(--blue); }

.footer-social-icon {
  width:  20px;
  height: 20px;
}

/* Foundation block credit — "Foundation Block [761336]" at the right
   end of the bottom row. The name came out of it on 11 September 2026:
   it is spelled out once, in the wordmark. */
.footer-foundation {
  font-family: var(--font-ui);
  font-size:   12px;
  font-weight: 300;
  color:       var(--text-label);
  white-space: nowrap;
}

.footer-foundation-link {
  color:           var(--orange);
  text-decoration: none;
}

.footer-foundation-link:hover { color: var(--blue); }

/* ── Back-to-top — a tab hanging from the footer's top line, on the
   centre, where the rule between the halves also lands. No top border,
   so it hangs out of the bright line rather than sitting on it. Grey
   at rest and blue on hover, like every standard button. Click jumps
   to the first content section. Sits absolutely at the top of .footer
   (which has position: relative). */
.footer-to-top {
  position:        absolute;
  left:            50%;
  transform:       translateX(-50%);
  width:           90px;
  height:          26px;
  display:         flex;
  align-items:     center;
  justify-content: center;
  border:          1px solid var(--border);
  border-top:      0;
  background:      var(--rest);
  color:           var(--text-label);
  text-decoration: none;
  transition:      color 0.5s ease, border-color 0.5s ease, background 0.5s ease, box-shadow 0.5s ease;
  z-index:         1;
}

.footer-to-top:hover,
.footer-to-top:focus-visible {
  z-index:      var(--z-overlay);
  color:        var(--blue);
  border-color: var(--blue);
  background:   var(--hover);
  box-shadow:   var(--shadow);
  outline:      none;
}

.footer-to-top-icon {
  width:  16px;
  height: 16px;
}

/* ── Shop grid — product card grid for /shop. Placeholder thumbs
   show `// 01`–`// 06` until real product photography lands. */
.shop {
  max-width: 1200px;
  margin:    0 auto;
  padding:   calc(var(--header-height) + 80px) 24px 120px;
  position:  relative;
  z-index:   1;
}

.shop-title {
  margin-bottom: 40px;
  color:         var(--text-heading);
  /* The glow comes from the type class beside it; this rule carried
     its own copy of recipe 04 until 15 September 2026. */
}

.shop-grid {
  display:               grid;
  grid-template-columns: repeat(3, 1fr);
  gap:                   24px;
}

.shop-card {
  display:         flex;
  flex-direction:  column;
  border:          1px solid var(--border);
  background:      var(--rest);
  color:           inherit;
  text-decoration: none;
  transition:      color 0.5s ease, border-color 0.5s ease, background 0.5s ease, box-shadow 0.5s ease;
}

.shop-card:hover {
  border-color: var(--blue);
  background:   var(--hover);
  box-shadow:   var(--shadow);
}

.shop-card-thumb {
  aspect-ratio:   1;
  display:        flex;
  align-items:    center;
  justify-content:center;
  background:     var(--bg);
  font-family:    var(--font-ui);
  font-size:      14px;
  letter-spacing: 0.18em;
  color:          var(--text-label);
  overflow:       hidden;
}

.shop-card-thumb img {
  width:      100%;
  height:     100%;
  object-fit: cover;
  display:    block;
}

.shop-card-info {
  padding:        16px 20px 20px;
  display:        flex;
  flex-direction: column;
  gap:            4px;
}

.shop-card-name {
  color: var(--text-heading);
}

.shop-card-price {
  color: var(--green);
}




/* ============================================================
   7. BUTTONS

   TWO MODES:

   IMPORTANT — color visible at rest (primary actions):
   <button class="btn btn-orange">BUY NOW</button>

   STANDARD — dark at rest, color reveals on hover (secondary):
   <button class="btn btn-standard-orange">VIEW MORE</button>

   NEUTRAL — no brand color, just grey:
   <button class="btn btn-neutral">CANCEL</button>

   Available colors:
     btn-orange · btn-blue · btn-red · btn-green · btn-yellow
     btn-standard-orange · btn-standard-blue · btn-standard-red
     btn-standard-green · btn-standard-yellow
     btn-neutral

   Add "disabled" attribute to disable any button.
   Disabled buttons stay grey on hover — cursor shows not-allowed.

   Hover on a button that rests in color: the 10% black wash and a
   quiet glow in its own color, 0 0 12px 3px at 0.2 (owner, 15 September
   2026). The same glow sits on the other color-at-rest buttons: the
   footer's Checkout and both cart totals. And on a current state in
   its color: the side nav's active item and parked //, the header's
   cart while open (owner, 15 September 2026).
============================================================ */


/* ── Base — shared by every button ── */

.btn {
  position:                   relative;
  z-index:                    var(--z-content);
  display:                    flex;
  align-items:                center;
  justify-content:            center;
  width:                      100%;
  height:                     60px;
  padding-top:                0;
  padding-bottom:             0;
  padding-left:               20px;
  padding-right:              20px;
  border-width:               1px;
  border-style:               solid;
  border-color:               transparent;
  border-radius:              0;
  background-color:           var(--rest);
  cursor:                     pointer;
  outline:                    none;
  white-space:                nowrap;
  font-family:                var(--font-ui);
  font-size:                  21px;
  font-weight:                400;
  letter-spacing:             0.06em;
  text-transform:             uppercase;
  text-decoration:            none;
  transition-property:        background-color, color, border-color, box-shadow;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

/* ── Focus ring ── */
.btn:focus-visible {
  outline:        1px solid var(--orange);
  outline-offset: -2px;
}


/* ════════════════════════════════════════════
   IMPORTANT MODE — color visible at rest
════════════════════════════════════════════ */


/* ── Neutral — no brand color ── */

.btn-neutral {
  border-color: var(--text-label);
  color:        var(--text-label);
}

.btn-neutral:hover {
  background-color: var(--hover);
  border-color:     var(--text-body);
  color:            var(--text-body);
  box-shadow:       var(--shadow);
}

.btn-neutral:active {
  background-color: var(--rest);
  border-color:     var(--text-heading);
  color:            var(--text-heading);
  box-shadow:       var(--shadow);
}

.btn-neutral:disabled,
.btn-neutral:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Orange ── */

.btn-orange {
  border-color: var(--orange);
  color:        var(--orange);
}

.btn-orange:hover {
  background-color: var(--hover);
  border-color:     var(--orange);
  color:            var(--orange);
  box-shadow:       0 0 12px 3px rgba(227, 121, 27, 0.2);
}

.btn-orange:active {
  background-color: var(--active-orange);
  border-color:     var(--orange);
  color:            var(--orange);
  box-shadow:       var(--shadow);
}

.btn-orange:disabled,
.btn-orange:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Blue ── */

.btn-blue {
  border-color: var(--blue);
  color:        var(--blue);
}

.btn-blue:hover {
  background-color: var(--hover);
  border-color:     var(--blue);
  color:            var(--blue);
  box-shadow:       0 0 12px 3px rgba(47, 53, 224, 0.2);
}

.btn-blue:active {
  background-color: var(--active-blue);
  border-color:     var(--blue);
  color:            var(--blue);
  box-shadow:       var(--shadow);
}

.btn-blue:disabled,
.btn-blue:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Red ── */

.btn-red {
  border-color: var(--red);
  color:        var(--red);
}

.btn-red:hover {
  background-color: var(--hover);
  border-color:     var(--red);
  color:            var(--red);
  box-shadow:       0 0 12px 3px rgba(224, 48, 48, 0.2);
}

.btn-red:active {
  background-color: var(--active-red);
  border-color:     var(--red);
  color:            var(--red);
  box-shadow:       var(--shadow);
}

.btn-red:disabled,
.btn-red:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Yellow ── */

.btn-yellow {
  border-color: var(--yellow);
  color:        var(--yellow);
}

.btn-yellow:hover {
  background-color: var(--hover);
  border-color:     var(--yellow);
  color:            var(--yellow);
  box-shadow:       0 0 12px 3px rgba(216, 208, 22, 0.2);
}

.btn-yellow:active {
  background-color: var(--active-yellow);
  border-color:     var(--yellow);
  color:            var(--yellow);
  box-shadow:       var(--shadow);
}

.btn-yellow:disabled,
.btn-yellow:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Green ── */

.btn-green {
  border-color: var(--green);
  color:        var(--green);
}

.btn-green:hover {
  background-color: var(--hover);
  border-color:     var(--green);
  color:            var(--green);
  box-shadow:       0 0 12px 3px rgba(50, 180, 67, 0.2);
}

.btn-green:active {
  background-color: var(--active-green);
  border-color:     var(--green);
  color:            var(--green);
  box-shadow:       var(--shadow);
}

.btn-green:disabled,
.btn-green:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ════════════════════════════════════════════
   STANDARD MODE — dark at rest, color on hover
   One class does everything. No conflicts.
════════════════════════════════════════════ */

/* ── Standard Neutral ── */

.btn-standard-neutral {
  border-color: var(--border);
  color:        var(--text-label);
}

.btn-standard-neutral:hover {
  background-color: var(--hover);
  border-color:     var(--text-label);
  color:            var(--text-label);
  box-shadow:       var(--shadow);
}

.btn-standard-neutral:active {
  background-color: var(--rest);
  border-color:     var(--text-label);
  color:            var(--text-label);
  box-shadow:       var(--shadow);
}

.btn-standard-neutral:disabled,
.btn-standard-neutral:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Standard Orange ── */

.btn-standard-orange {
  border-color: var(--border);
  color:        var(--text-label);
}

.btn-standard-orange:hover {
  background-color: var(--hover);
  border-color:     var(--orange);
  color:            var(--orange);
  box-shadow:       var(--shadow);
}

.btn-standard-orange:active {
  background-color: var(--active-orange);
  border-color:     var(--orange);
  color:            var(--orange);
  box-shadow:       var(--shadow);
}

.btn-standard-orange:disabled,
.btn-standard-orange:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Standard Blue ── */

.btn-standard-blue {
  border-color: var(--border);
  color:        var(--text-label);
}

.btn-standard-blue:hover {
  background-color: var(--hover);
  border-color:     var(--blue);
  color:            var(--blue);
  box-shadow:       var(--shadow);
}

.btn-standard-blue:active {
  background-color: var(--active-blue);
  border-color:     var(--blue);
  color:            var(--blue);
  box-shadow:       var(--shadow);
}

.btn-standard-blue:disabled,
.btn-standard-blue:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Standard Red ── */

.btn-standard-red {
  border-color: var(--border);
  color:        var(--text-label);
}

.btn-standard-red:hover {
  background-color: var(--hover);
  border-color:     var(--red);
  color:            var(--red);
  box-shadow:       var(--shadow);
}

.btn-standard-red:active {
  background-color: var(--active-red);
  border-color:     var(--red);
  color:            var(--red);
  box-shadow:       var(--shadow);
}

.btn-standard-red:disabled,
.btn-standard-red:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Standard Yellow ── */

.btn-standard-yellow {
  border-color: var(--border);
  color:        var(--text-label);
}

.btn-standard-yellow:hover {
  background-color: var(--hover);
  border-color:     var(--yellow);
  color:            var(--yellow);
  box-shadow:       var(--shadow);
}

.btn-standard-yellow:active {
  background-color: var(--active-yellow);
  border-color:     var(--yellow);
  color:            var(--yellow);
  box-shadow:       var(--shadow);
}

.btn-standard-yellow:disabled,
.btn-standard-yellow:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}


/* ── Standard Green ── */

.btn-standard-green {
  border-color: var(--border);
  color:        var(--text-label);
}

.btn-standard-green:hover {
  background-color: var(--hover);
  border-color:     var(--green);
  color:            var(--green);
  box-shadow:       var(--shadow);
}

.btn-standard-green:active {
  background-color: var(--active-green);
  border-color:     var(--green);
  color:            var(--green);
  box-shadow:       var(--shadow);
}

.btn-standard-green:disabled,
.btn-standard-green:disabled:hover {
  background-color: var(--overlay-20);
  border-color:     var(--disabled);
  color:            var(--disabled);
  box-shadow:       none;
  cursor:           not-allowed;
}



/* ============================================================
   8. ACCORDION
   Click to expand/collapse content rows.
   JS toggles the "open" class on .acc-item.

   HOW TO USE:
   <div class="acc">
     <div class="acc-item" role="listitem">
       <div class="acc-header">
         <button class="acc-btn" aria-expanded="false"
                 aria-controls="acc-body-1" id="acc-btn-1">
           <span class="acc-left" aria-hidden="true">
             <span class="acc-plus">+</span><span class="acc-minus">-</span>
           </span>
           <span class="acc-title text-h3-21px">Your title here</span>
         </button>
       </div>
       <div class="acc-body" id="acc-body-1"
            role="region" aria-labelledby="acc-btn-1">
         <div class="acc-content">
           <div class="acc-inner text-body-18px">Your content here.</div>
         </div>
       </div>
     </div>
   </div>

   The + and - are typed OCR-Bitcoin characters, as the fields use:
   a plain hyphen, never U+2212, which this font does not carry.
   The title takes no glow.
============================================================ */

/* ── Outer wrapper ── */
.acc {
  width:       100%;
  font-family: var(--font-ui);
}

/* ── One row ── */
.acc-item {
  position:   relative;
  margin-top: -1px;
  z-index:    0;
}

.acc-item:first-child { margin-top: 0; }

/* ── Hovered closed item lifts above other closed items (still below the open item) ── */
.acc-item:not(.open):has(.acc-btn:hover) { z-index: var(--z-overlay); }

/* ── Open item sits above everything else in the accordion ── */
.acc-item.open { z-index: var(--z-active); }

.acc-header {
  position:                   relative;
  z-index:                    var(--z-active);
  border-width:               1px;
  border-style:               solid;
  border-color:               var(--border);        /* rest */
  transition-property:        border-color;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

.acc-header:has(.acc-btn:hover)           { border-color: var(--blue); }    /* hover */
.acc-item.open .acc-header                { border-color: var(--orange); }  /* open */
.acc-item.open .acc-header:has(.acc-btn:hover) { border-color: var(--orange); } /* open+hover */

.acc-btn {
  width:                      100%;
  min-height:                 60px;   /* a long title wraps and the row grows */
  display:                    flex;
  align-items:                center;
  padding:                    0 20px 0 0;
  border-width:               0;
  border-radius:              0;
  background-color:           var(--rest);
  color:                      var(--text-label);         /* rest */
  text-align:                 left;
  cursor:                     pointer;
  outline:                    none;
  transition-property:        background-color, color, box-shadow;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

.acc-btn:hover {
  background-color: var(--hover);
  color:            var(--blue);                    /* hover */
  box-shadow:       var(--shadow);
}

.acc-item.open .acc-btn {
  background-color: var(--active-orange);
  color:            var(--orange);                  /* open */
}

.acc-item.open .acc-btn:hover {
  background-color: var(--hover);
  color:            var(--orange);
  box-shadow:       var(--shadow);
}

.acc-btn:focus-visible {
  outline:        1px solid var(--orange);
  outline-offset: -2px;
}

/* ── The + and - column: typed OCR-Bitcoin at 21, the row's color ── */
.acc-left {
  width:           60px;
  height:          60px;
  flex-shrink:     0;
  display:         flex;
  align-items:     center;
  justify-content: center;
  padding-top:     3px;   /* the OCR-Bitcoin nudge */
  font-family:     var(--font-btc);
  font-size:       21px;
  line-height:     1;
  letter-spacing:  0;
}

.acc-minus                    { display: none; }
.acc-item.open .acc-plus      { display: none; }
.acc-item.open .acc-minus     { display: inline; }

.acc-title {
  flex:           1;
  min-width:      0;
  padding:        var(--gap-10) 0 var(--gap-10) var(--gap-20);   /* 10 above and below once it wraps; one line still sits in 60 */
  overflow-wrap:  anywhere;
}

/* The title wears .text-h3-21px; the row's state colors still win,
   and an accordion title takes no glow (owner, 16 September 2026) */
.acc-title.text-h3-21px {
  color:       inherit;
  text-shadow: none;
}

.acc-body {
  position:                   relative;
  z-index:                    var(--z-content);
  overflow:                   hidden;
  max-height:                 0;
  transition-property:        max-height;
  transition-duration:        0.5s;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

.acc-content {
  border-width:     1px;
  border-style:     solid;
  border-color:     var(--orange);                  /* open border */
  border-top-width: 0;
  background-color: var(--active-orange);
}

.acc-inner {
  padding:          var(--gap-30) var(--gap-30) var(--gap-30) var(--gap-80);
  background-color: var(--overlay-50);
}




/* ============================================================
   9. COPY ROW
   Click-to-copy address rows (Bitcoin address, npub, etc.)
   CSS owns every visual; JS only transitions the data-state attribute.
   See the four .copy-row[data-state="..."] blocks below + the COPY ROW
   handler in script.js. Optional wrap/action extension lives further
   down this section (.copy-row-wrap + .copy-row-action + .reveal-row),
   used by the contact page to pair each row with an "open in app" link.

   STATES:
   rest   → dark border, muted icon                  (base .copy-row rules)
   hover  → blue border and icon                     ([data-state="hover"])
   copied → green pulse + halo, // copied label flash ([data-state="copied"])
   done   → orange border + bg, orange pill on text  ([data-state="done"])

   HOW TO USE:
   <button class="copy-row" aria-label="Copy to clipboard"
           data-copy="your-value-here">
     <span class="copy-icon-cell">
       <svg class="copy-icon" viewBox="0 0 14 14" fill="none"
            xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
         <rect x="4" y="4" width="8" height="9"
               stroke="currentColor" stroke-width="1"/>
         <path d="M2 10V1H10" stroke="currentColor" stroke-width="1"/>
       </svg>
     </span>
     <span class="copy-clip">
       <span class="copy-value">
         <span class="copy-text text-data-16px"></span>
         <span class="copy-label text-data-16px">// copied</span>
       </span>
     </span>
   </button>

   Both .copy-text and .copy-label carry one 16 readout class in
   OCR-Bitcoin, so the // copied pill keeps the address's stride. The
   elements page uses .text-copy-16px, the contact page .text-data-16px;
   the compound .copy-label rules below keep the pill text dark on green
   under either.

   Repeat the button markup for each address. script.js's COPY ROW
   handler runs on every page; nothing to add per page.

   KEYBOARD: a row reached with Tab shows the hover look, so the row
   in focus can be seen.
============================================================ */

/* ── Outer button row ──
   Default border/bg/color match the rest state set by JS. Without these,
   the row falls back to currentColor and looks broken if JS hasn't run. */
.copy-row {
  display:            flex;
  align-items:        stretch;
  width:              100%;
  cursor:             pointer;
  outline:            none;
  padding:            0;
  -webkit-appearance: none;
  appearance:         none;
  border-width:       1px;
  border-style:       solid;
  border-color:       var(--border);
  background-color:   var(--rest);
  color:              var(--text-label);
  transition: border-color 0.5s ease, background-color 0.5s ease, box-shadow 0.5s ease;
}

.copy-icon-cell {
  flex-shrink:        0;
  display:            flex;
  align-items:        center;
  justify-content:    center;
  width:              60px;
  height:             60px;
  border-right-width: 1px;
  border-right-style: solid;
  border-right-color: var(--border);
  color:              inherit;
  transition: border-color 0.5s ease, color 0.5s ease;
}

.copy-icon {
  width:       16px;
  height:      16px;
  flex-shrink: 0;
}

/* The icon's lines are 2 on screen, set in screen pixels rather than the
   drawing's units, to sit with the accordion's typed + and - (owner,
   16 September 2026). Wins over the stroke-width in the markup. */
.copy-icon rect,
.copy-icon path {
  vector-effect: non-scaling-stroke;
  stroke-width:  2px;
}

/* ── Text clip container — hides overflow ── */
.copy-clip {
  flex:         1;
  min-width:    0;
  height:       60px;
  position:     relative;
  overflow:     hidden;
  margin-right: var(--gap-20);   /* keeps text away from right border */
}

/* ── Scrollable value row ── */
.copy-value {
  position:       relative;   /* the flash is placed against this box, above the scroll bar */
  width:          100%;
  height:         100%;
  display:        flex;
  align-items:    center;
  overflow-x:     auto;
  overflow-y:     hidden;
  cursor:         pointer;
  padding-left:   var(--gap-10);   /* with the address's own 10, the letters start 20 in */
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

.copy-value::-webkit-scrollbar        { height: 4px; }
.copy-value::-webkit-scrollbar-track  { background: transparent; }
.copy-value::-webkit-scrollbar-thumb  { background: var(--border); border-radius: 0; }

/* ── Address text — typography lives on .text-copy-16px ── */
/* The address carries the pill's padding in every state, clear at rest
   and orange once done, so the letters never move when the pill fills */
.copy-text {
  white-space:    nowrap;
  display:        flex;
  align-items:    center;
  flex-shrink:    0;
  height:         30px;   /* the pill sits on the scale; the text centers in it */
  padding:        0 var(--gap-10);
  margin-right:   var(--gap-20);
  transition: background-color 0.5s ease, color 0.5s ease;
}

/* ── // copied flash label — typography lives on .text-copy-16px;
       line-height / white-space stay here because the pill has fixed
       geometry that the shared token can't enforce. Label content is
       authored as the literal "// copied" microcopy (matches the site's
       // kicker pattern); no text-transform — let the casing render
       as written. */
/* The flash sits over the address instead of replacing it. The address
   stays in the row, hidden, so a long one keeps its scroll bar through
   every state and nothing moves when the flash comes and goes (owner,
   14 September 2026). Placed against .copy-value, whose box ends above
   the scroll bar, so the flash is centered on the same line as the
   address; script.js scrolls the row back to its start on the click.

   One text line in every state (owner, 14 September 2026): the letters
   start 20 in, at rest, in the green flash and in the orange pill. Both
   pills are the same shape, 10 each side, and reach 10 out to the left
   of the letters, so the letters stay put and only the color changes. */
.copy-label {
  display:        none;
  position:       absolute;
  top:            50%;
  left:           var(--gap-10);
  transform:      translateY(-50%);
  white-space:    nowrap;
  align-items:    center;
  height:         30px;   /* the same pill as the address */
  padding:        0 var(--gap-10);
  background:     var(--green);
  color:          var(--bg);
}

/* Bump past .text-copy-16px { color: inherit }, which would otherwise win
   on source order and pull the // copied text to the row's state color. */
.copy-label.text-copy-16px { color: var(--bg); }

/* The address and the flash are both in the readout font in every state
   (owner, 14 September 2026). The font comes from .text-copy-16px itself,
   OCR-Bitcoin since the lock the same day; nothing to override here. */


/* ── State-driven styling ──
   The .copy-row base rules above define the REST default (border, bg,
   text/icon colors, scrollbar). The four state blocks below paint
   hover / copied / done by reading the [data-state] attribute that
   script.js sets. No inline styles — CSS owns every state outcome.
   Hover takes --shadow; the green copied pulse takes the quiet color
   glow from section 7. */

.copy-row[data-state="hover"],
.copy-row:focus-visible                          { border-color: var(--blue);   background-color: var(--hover);        box-shadow: var(--shadow); }
.copy-row[data-state="hover"]  .copy-icon-cell,
.copy-row:focus-visible        .copy-icon-cell  { border-right-color: var(--blue);   color: var(--blue);   }
.copy-row[data-state="hover"]  .copy-value,
.copy-row:focus-visible        .copy-value      { scrollbar-color: var(--blue)   transparent; }

.copy-row[data-state="copied"]                  { border-color: var(--green);  background-color: var(--active-green); box-shadow: 0 0 12px 3px rgba(50, 180, 67, 0.2); }   /* quiet color glow, section 7 */
.copy-row[data-state="copied"] .copy-icon-cell  { border-right-color: var(--green);  color: var(--green);  }
.copy-row[data-state="copied"] .copy-value      { scrollbar-color: var(--green)  transparent; }
.copy-row[data-state="copied"] .copy-text       { visibility: hidden; }
.copy-row[data-state="copied"] .copy-label      { display: flex; }

.copy-row[data-state="done"]                    { border-color: var(--orange); background-color: var(--active-orange); box-shadow: none; }
.copy-row[data-state="done"]   .copy-icon-cell  { border-right-color: var(--orange); color: var(--orange); }
.copy-row[data-state="done"]   .copy-value      { scrollbar-color: var(--orange) transparent; }
.copy-row[data-state="done"]   .copy-text       { background-color: var(--orange); color: var(--bg); }

/* Hover on a copied row: the locked hover for a thing that rests in
   color, the black wash and the quiet glow in its own orange (owner,
   16 September 2026). The orange line stays. */
.copy-row[data-state="done"]:hover              { background-color: var(--hover); box-shadow: 0 0 12px 3px rgba(227, 121, 27, 0.2); }   /* quiet color glow, section 7 */


/* ── Copy-row WRAP + ACTION ──
   Wrap pairs a .copy-row (or .reveal-row) with an attached right-side
   action — modeled on the testimonial verify column. The seam is
   colored by the action's left border (offset -1px), so the action
   can light up GREEN ("opened / copied") while the row keeps its own
   color (ORANGE done, etc.). Used by the contact page's three
   channels and any future "data row + open in app" pair. */
.copy-row-wrap {
  display:     flex;
  align-items: stretch;
}
.copy-row-wrap .copy-row,
.copy-row-wrap .reveal-row {
  flex:      1;
  min-width: 0;
  width:     auto;
}

.copy-row-action {
  flex-shrink:      0;
  width:            60px;
  display:          flex;
  align-items:      center;
  justify-content:  center;
  position:         relative;
  z-index:          1;
  margin-left:      -1px;
  border:           1px solid var(--border);
  background-color: var(--rest);
  color:            var(--text-label);
  text-decoration:  none;
  cursor:           pointer;
  /* 0.12s, not the 0.5s used elsewhere: the COPY ROW handler's copied
     state lasts only 600ms before flipping to done, so a slow transition
     would barely show the green before it locks in. */
  transition: border-color 0.12s ease, background-color 0.12s ease, color 0.12s ease;
}
.copy-row-action:hover,
.copy-row-action:focus-visible {
  border-color:     var(--blue);
  background-color: var(--hover);
  color:            var(--blue);
}
.copy-row-action[aria-disabled="true"] {
  opacity:        0.25;
  pointer-events: none;
}
.copy-row-action-icon { width: 20px; height: 20px; }

/* State mirror — action reads its sibling row's data-state (set by
   script.js's COPY ROW handler) and the reveal-row's data-revealed.
   Hover stays blue; copied / done / revealed all use the testimonial
   verify-green because the action is a "successfully copied / opened"
   confirmation, not the current selection. */
.copy-row-wrap .copy-row[data-state="hover"]  ~ .copy-row-action {
  border-color:     var(--blue);
  background-color: var(--hover);
  color:            var(--blue);
}
.copy-row-wrap .copy-row[data-state="copied"] ~ .copy-row-action,
.copy-row-wrap .copy-row[data-state="done"]   ~ .copy-row-action,
.reveal-row[data-revealed="true"]             ~ .copy-row-action {
  border-color:     var(--green);
  background-color: var(--active-green);
  color:            var(--green);
}

/* Inside a wrap, the row's "done" state goes back to neutral — only
   the orange pill on .copy-text (from the base done rule) and the green
   action sibling carry the state. The 600ms 'copied' pulse keeps the
   full green halo before this kicks in. Specificity (0,3,0) beats the
   base done rule (0,2,0). */
.copy-row-wrap .copy-row[data-state="done"]                 { border-color: var(--border); background-color: var(--rest); box-shadow: none; }
.copy-row-wrap .copy-row[data-state="done"] .copy-icon-cell { border-right-color: var(--border); color: var(--text-label); }
.copy-row-wrap .copy-row[data-state="done"] .copy-value     { scrollbar-color: var(--border) transparent; }

/* Post-copy hover — once the row is "already marked" (orange pill +
   green action), hovering adds only a soft blue glow, never a blue
   border (the blue outline is the rest-state hover affordance — wrong
   signal here). Same shape on .reveal-row when revealed. */
.copy-row-wrap .copy-row[data-state="done"]:hover {
  background-color: var(--hover);
  box-shadow:       0 0 12px 3px var(--active-blue);
}


/* ── REVEAL ROW (email pattern) ──
   Mirrors .copy-row's rest look, but uses its own class so the COPY ROW
   handler (which sets text from data-copy) doesn't overwrite the dots
   or stash a stale value. The EMAIL REVEAL handler in script.js owns
   the click; the OUTSIDE-CLICK RESET handler rolls it back. */
.reveal-row {
  display:            flex;
  align-items:        stretch;
  width:              100%;
  cursor:             pointer;
  outline:            none;
  padding:            0;
  -webkit-appearance: none;
  appearance:         none;
  border:             1px solid var(--border);
  background-color:   var(--rest);
  color:              var(--text-body);
  transition: border-color 0.5s ease, background-color 0.5s ease, color 0.5s ease;
}
.reveal-row .copy-icon-cell {
  color: var(--text-label);
  transition: color 0.5s ease, border-right-color 0.5s ease;
}
.reveal-row:hover,
.reveal-row:focus-visible {
  border-color:     var(--blue);
  background-color: var(--hover);
}
.reveal-row:hover .copy-icon-cell,
.reveal-row:focus-visible .copy-icon-cell {
  color:              var(--blue);
  border-right-color: var(--blue);
}
.reveal-row[data-revealed="true"]:hover {
  border-color:     var(--border);
  background-color: var(--hover);
  box-shadow:       0 0 12px 3px var(--active-blue);
}
.reveal-row[data-revealed="true"]:hover .copy-icon-cell {
  color:              var(--text-label);
  border-right-color: var(--border);
}

/* Email green-pulse state — mirrors .copy-row[data-state="copied"] so
   reveal+copy on email feels identical to X/NOSTR. The handler sets
   data-copying on click, clears it after 600ms. The compound
   [data-revealed][data-copying] selector beats [data-revealed]:hover
   by specificity (0,3,0 vs 0,2,1) so the green wins during the pulse. */
.reveal-row[data-revealed="true"][data-copying="true"] {
  border-color:     var(--green);
  background-color: var(--active-green);
  box-shadow:       0 0 12px 3px rgba(50, 180, 67, 0.2);   /* quiet color glow, section 7 */
}
.reveal-row[data-revealed="true"][data-copying="true"] .copy-icon-cell {
  border-right-color: var(--green);
  color:              var(--green);
}
.reveal-row[data-copying="true"] .copy-text  { visibility: hidden; }
.reveal-row[data-copying="true"] .copy-label { display: flex; }

.reveal-dots, .reveal-email { display: none; }
.reveal-row[data-revealed="false"] .reveal-dots { display: inline; }
.reveal-row[data-revealed="true"]  .reveal-email { display: inline; }

/* Revealed state — orange pill on the address mirrors .copy-row's
   "done" state on .copy-text, so a revealed email reads as "actioned"
   the same way a copied X/NOSTR row does. The pill is the address's own
   box, as on the copy row, so the letters start 20 in like every row. */
.reveal-row[data-revealed="true"] .copy-text {
  background: var(--orange);
  color:      var(--bg);
}

/* Bump past .text-copy-16px / .text-data-16px { color: inherit }, which
   would otherwise pull the // copied text to the row's state color.
   Mirror of the same bump for .copy-label.text-copy-16px above. */
.copy-label.text-data-16px { color: var(--bg); }




/* ============================================================
   10. TYPE
   Locked typographic system — v4, 6 September 2026.
   20 classes · 11 sizes: 12 14 16 18 21 24 36 48 60 96 144
   Weight 300 no longer appears in the scale; everything under 16 is regular.
   Titles glow: display 1 carries recipe 04, every other display and
   heading class carries recipe 05 (see .dev/title-glow.html).
   Italic only in .text-tagline-36px. Inline emphasis = orange color swap.
   Line height and letter spacing locked 14 September 2026 (owner, line 4
   and letters 6); the rule is in Website-Aesthetics, Type.

   DISPLAY (Big Shoulders Display, weight 900, hero only):
   .text-display1-144px
   .text-display2-96px
   .text-display3-60px
   .text-display4-48px — the step between 60 and 36

   HEADING (IBM Plex Mono, weight 600, uppercase):
   .text-h1-36px
   .text-h2-24px
   .text-h3-21px

   TAGLINE (IBM Plex Mono, weight 600 italic, sentence case):
   .text-tagline-36px — sits under a display word

   BTC / DATA (OCR-Bitcoin, color signals meaning):
   .text-block-36px   — block height / large data
   .text-sats-21px    — sat amounts (body color)
   .text-btc-21px     — BTC amounts (orange)
   .text-usd-21px     — USD amounts (green)
   .text-copy-16px    — shared inside .copy-row, color inherits

   BODY / UI (IBM Plex Mono):
   .text-body-18px    — body copy (sentence case)
   .text-sub-16px     — kicker subtitle, orange border-left
   .text-label-14px   — labels, tags
   .text-fine-12px    — fine print
============================================================ */

/* ── Display — Big Shoulders 900 only, hero use.
   Sizes are FLUID via clamp(): the floor is a step on the type
   scale, the ceiling is the class's named pixel size, vw mid scales
   smoothly between. The named suffix is the MAX, not a fixed render
   size. ── */
.text-display1-144px {
  font-family:    var(--font-display);
  font-weight:    900;
  font-size:      clamp(60px, 14vw, 144px);
  line-height:    1.0;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color:          var(--text-heading);
  /* Display glow, recipe 04 on .dev/title-glow.html, the default since
     5 September 2026: double cream core, strong bloom, wide falloff. */
  text-shadow:
    0 0  1px rgba(245, 243, 239, 1),
    0 0  3px rgba(245, 243, 239, 0.6),
    0 0 10px rgba(227, 121,  27, 0.9),
    0 0 30px rgba(227, 121,  27, 0.5),
    0 0 80px rgba(227, 121,  27, 0.25);
}
.text-display2-96px {
  font-family:    var(--font-display);
  font-weight:    900;
  font-size:      clamp(48px, 10vw, 96px);
  line-height:    1.0;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color:          var(--text-heading);
  /* Small-title glow, recipe 05 on .dev/title-glow.html: 1px cream core,
     small orange bloom, short falloff. On every title below display 1
     since 6 September 2026. */
  text-shadow:
    0 0  1px rgba(245, 243, 239, 1),
    0 0  6px rgba(227, 121,  27, 0.8),
    0 0 20px rgba(227, 121,  27, 0.35);
}
/* Display 3 moved from 72 to 60, 11 September 2026: the footer wordmark
   wanted a step between 48 and 72 and 72 was on no page, so the step
   itself moved rather than a twelfth size being added. The name moved
   with the size, as the class system requires. */
.text-display3-60px {
  font-family:    var(--font-display);
  font-weight:    900;
  font-size:      clamp(36px, 8vw, 60px);
  line-height:    1.0;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color:          var(--text-heading);
  /* Small-title glow, recipe 05 on .dev/title-glow.html: 1px cream core,
     small orange bloom, short falloff. On every title below display 1
     since 6 September 2026. */
  text-shadow:
    0 0  1px rgba(245, 243, 239, 1),
    0 0  6px rgba(227, 121,  27, 0.8),
    0 0 20px rgba(227, 121,  27, 0.35);
}
.text-display4-48px {
  font-family:    var(--font-display);
  font-weight:    900;
  font-size:      clamp(36px, 5vw, 48px);
  line-height:    1.0;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color:          var(--text-heading);
  /* Small-title glow, recipe 05 on .dev/title-glow.html: 1px cream core,
     small orange bloom, short falloff. On every title below display 1
     since 6 September 2026. */
  text-shadow:
    0 0  1px rgba(245, 243, 239, 1),
    0 0  6px rgba(227, 121,  27, 0.8),
    0 0 20px rgba(227, 121,  27, 0.35);
}


/* ── Heading — Plex SemiBold, uppercase. Fluid like the displays.
   Every floor is a step on the type scale (owner, 14 September 2026);
   the rule is in Website-Aesthetics, Type. ── */
.text-h1-36px {
  font-family:    var(--font-ui);
  font-weight:    600;
  font-size:      clamp(24px, 4vw, 36px);
  line-height:    1.5;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color:          var(--text-heading);
  /* Small-title glow, recipe 05 on .dev/title-glow.html: 1px cream core,
     small orange bloom, short falloff. On every title below display 1
     since 6 September 2026. */
  text-shadow:
    0 0  1px rgba(245, 243, 239, 1),
    0 0  6px rgba(227, 121,  27, 0.8),
    0 0 20px rgba(227, 121,  27, 0.35);
}
.text-h2-24px {
  font-family:    var(--font-ui);
  font-weight:    600;
  font-size:      24px;
  line-height:    1.5;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color:          var(--text-heading);
  /* Small-title glow, recipe 05 on .dev/title-glow.html: 1px cream core,
     small orange bloom, short falloff. On every title below display 1
     since 6 September 2026. */
  text-shadow:
    0 0  1px rgba(245, 243, 239, 1),
    0 0  6px rgba(227, 121,  27, 0.8),
    0 0 20px rgba(227, 121,  27, 0.35);
}
.text-h3-21px {
  font-family:    var(--font-ui);
  font-weight:    600;
  font-size:      21px;
  line-height:    1.5;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color:          var(--text-heading);
  /* Small-title glow, recipe 05 on .dev/title-glow.html: 1px cream core,
     small orange bloom, short falloff. On every title below display 1
     since 6 September 2026. */
  text-shadow:
    0 0  1px rgba(245, 243, 239, 1),
    0 0  6px rgba(227, 121,  27, 0.8),
    0 0 20px rgba(227, 121,  27, 0.35);
}

/* Tagline — Plex SemiBold Italic, sentence case. Sits under a display word,
   per the brand sheet. Not uppercase, not for headings. */
.text-tagline-36px {
  font-family:    var(--font-ui);
  font-weight:    600;
  font-style:     italic;
  font-size:      clamp(24px, 4vw, 36px);
  line-height:    1.5;
  letter-spacing: 0.02em;
  color:          var(--text-heading);
}

/* ── BTC / Data — OCR-Bitcoin, color signals meaning ── */
.text-block-36px {
  font-family:    var(--font-btc);
  font-size:      36px;
  line-height:    1.5;
  letter-spacing: 0.02em;
  color:          var(--text-heading);
}
.text-sats-21px {
  font-family:    var(--font-btc);
  font-size:      21px;
  line-height:    1.5;
  letter-spacing: 0.06em;
  color:          var(--text-body);
}
.text-btc-21px {
  font-family:    var(--font-btc);
  font-size:      21px;
  line-height:    1.5;
  letter-spacing: 0.06em;
  color:          var(--orange);
}
.text-usd-21px {
  font-family:    var(--font-btc);
  font-size:      21px;
  line-height:    1.5;
  letter-spacing: 0.06em;
  color:          var(--green);
}
.text-data-18px {
  font-family:    var(--font-btc);
  font-size:      18px;
  line-height:    1.5;
  letter-spacing: 0.08em;
  color:          var(--text-body);
}
.text-data-16px {
  font-family:    var(--font-btc);
  font-size:      16px;
  line-height:    1.5;
  letter-spacing: 0.10em;
  color:          var(--text-body);
}

/* The scale's smallest readout size. Added 7 September 2026 for the
   mini-cart's line money, which sits beside the 14px labels and has to
   match them. 14 was already on the type scale in Plex (.text-label-14px);
   this is the same step in the readout font. */
.text-data-14px {
  font-family:    var(--font-btc);
  font-size:      14px;
  line-height:    1.5;
  letter-spacing: 0.12em;
  color:          var(--text-body);
}

/* ── Body / UI — Plex Regular ── */
.text-body-18px {
  font-family:    var(--font-ui);
  font-weight:    400;
  font-size:      18px;
  line-height:    1.6;
  letter-spacing: 0;
  color:          var(--text-body);
}

/* ── The body copy measure — .column-text ──
   A block of running copy divides like a newspaper wherever a column
   can still hold 45 characters. The split is on the block, never on
   .text-body-18px. Locked 15 September 2026; rule in
   Website-Aesthetics, Type. The face and size are here only so that
   45ch is measured in the copy's own font. */
.column-text {
  font-family:  var(--font-ui);
  font-size:    18px;

  column-width: 45ch;
  column-gap:   var(--gap-60);
  column-rule:  1px solid var(--border);
  column-fill:  balance;   /* the columns end level */
  orphans:      2;         /* never strand one line at a column's foot */
  widows:       2;         /* nor at its head */
}

/* Air between the paragraphs. A bottom margin, not a flex gap or a
   top margin, or the columns will not divide or will not start level. */
.column-text > p               { margin-bottom: var(--gap-20); }
.column-text > p:last-child    { margin-bottom: 0; }

/* Shared typography for .copy-text and .copy-label inside .copy-row, and
   the mini-cart's subtotal. Anything a visitor copies or reads as a value
   is data, so this is the readout font (owner, locked 14 September 2026,
   trialed first on .dev/04-typography/typography.html).
   Color is intentionally inherited so the parent state machine drives it. */
.text-copy-16px {
  font-family:    var(--font-btc);
  font-size:      16px;
  line-height:    1.5;
  letter-spacing: 0.10em;
  color:          inherit;
}

/* Sub — kicker subtitle with orange accent bar */
.text-sub-16px {
  font-family:    var(--font-ui);
  font-weight:    400;
  font-size:      16px;
  line-height:    1.5;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color:          var(--text-body);
  border-left:    1px solid var(--orange);
  padding-left:   var(--gap-15);
}

.text-label-14px {
  font-family:    var(--font-ui);
  font-weight:    400;
  font-size:      14px;
  line-height:    1.5;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color:          var(--text-label);
}

.text-fine-12px {
  font-family:    var(--font-ui);
  font-weight:    400;
  font-size:      12px;
  line-height:    1.5;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color:          var(--text-label);
}


/* ============================================================
   11. LOGOS
   The OMG21 logo system. One sprite file holds 5 unique logos,
   each in their full brand colors by default. Mono and recolor
   treatments are achieved through CSS classes — no extra files.

   HOW TO USE:
   Reference any logo from the sprite by its symbol ID:

     <svg class="logo logo-h-64">
       <use href="assets/logo/omg21-logos.svg#lockup-full"/>
     </svg>

   AVAILABLE SYMBOL IDs:
     #lockup-full       symbol + wordmark — primary brand mark
     #wordmark-full     wordmark + small symbol mark
     #symbol-full       square symbol — two-tone
     #symbol-stacked    vertical stacked symbol — two-tone
     #symbol-compact    compact symbol — for tight spaces

   COLOR TREATMENTS:
     default           white primary + orange accent (full brand)
     .logo-mono        primary only — orange disappears
                       (use on busy backgrounds, in print, on packaging)

   SIZING:
     .logo-h-32 / .logo-h-48 / .logo-h-64 / .logo-h-96 / .logo-h-128
     Width auto-scales to maintain proportions.

   The sprite uses two internal classes (.logo-primary and .logo-accent)
   that bind to currentColor and var(--logo-accent-fill) respectively.
   The .logo wrapper sets both, so currentColor and the custom property
   inherit through <use> into the sprite. Mono works by overriding the
   --logo-accent-fill custom property to currentColor (selectors can't
   cross the <use> shadow boundary, but CSS variables can).
============================================================ */

/* ── Base: drives the full-color default ── */
.logo {
  display:                inline-block;
  color:                  var(--text-heading);   /* drives the primary fill via currentColor */
  --logo-accent-fill:     var(--orange);    /* drives the accent fill via CSS variable */
  vertical-align:         middle;
}

/* ── Mono override: hide the orange accent ── */
/* Setting --logo-accent-fill to currentColor makes the accent match primary, */
/* effectively producing a single-color logo. CSS custom properties cross the */
/* shadow boundary of <use>, which is why this approach works (selectors don't). */
.logo-mono { --logo-accent-fill: currentColor; }

/* ── Sizing utilities ── */
.logo-h-32  { height: 32px;  width: auto; }
.logo-h-48  { height: 48px;  width: auto; }
.logo-h-64  { height: 64px;  width: auto; }
.logo-h-96  { height: 96px;  width: auto; }
.logo-h-128 { height: 128px; width: auto; }

/* ── Logo specimen — pyramid hierarchy display ── */

.logo-pyramid {
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-20);
}

.logo-tier-1 { display: grid; grid-template-columns: 1fr; }
.logo-tier-2 { display: grid; grid-template-columns: 1fr 1fr; }
.logo-tier-3 { display: grid; grid-template-columns: repeat(3, 1fr); }
.logo-tier-4 { display: grid; grid-template-columns: repeat(4, 1fr); }

.logo-card {
  background:     var(--rest);
  border:         1px solid var(--border);
  display:        flex;
  flex-direction: column;
}

.logo-preview {
  flex:            1;
  display:         flex;
  align-items:     center;
  justify-content: center;
  padding:         48px 40px;
}

.logo-tier-1 .logo-preview { min-height: 300px; padding: 48px 0; }
.logo-tier-2 .logo-preview { padding: 40px; min-height: 220px; }
.logo-tier-3 .logo-preview { padding: 32px 24px; min-height: 160px; }
.logo-tier-4 .logo-preview { padding: 32px 24px; min-height: 160px; }

.logo-tier-1 .logo,
.logo-tier-2 .logo,
.logo-tier-3 .logo,
.logo-tier-4 .logo,
.logo-colors .logo {
  width:   calc(100% / 1.618);
  height:  auto;
  margin:  0 auto;
}

/* ── Mono color specimen — Symbol rendered in each primary accent ── */

.logo-colors {
  display:               grid;
  grid-template-columns: repeat(5, 1fr);
  gap:                   var(--gap-20);
}

.logo-colors .logo-preview { padding: 28px 20px; min-height: 160px; }

.logo-orange { color: var(--orange); }
.logo-yellow { color: var(--yellow); }
.logo-blue   { color: var(--blue);   }
.logo-green  { color: var(--green);  }
.logo-red    { color: var(--red);    }

.logo-preview .logo { display: block; max-width: 100%; }

.logo-meta {
  border-top:       1px solid var(--border);
  padding:          18px 24px;
  display:          flex;
  flex-direction:   column;
  gap:              6px;
  background-color: var(--overlay-20);
}

.logo-meta__name {
  font-family:     var(--font-ui);
  font-size:       12px;
  font-weight:     600;
  letter-spacing:  0.10em;
  text-transform:  uppercase;
  color:           var(--text-heading);
}

.logo-meta__role {
  font-family:     var(--font-btc);
  font-size:       12px;
  font-weight:     300;
  letter-spacing:  0.10em;
  text-transform:  uppercase;
  color:           var(--text-label);
}

/* ── Logo specimen — golden ratio spacing ── */

.logo-spacing {
  display:               grid;
  grid-template-columns: repeat(3, 1fr);
  gap:                   var(--gap-20);
}

.logo-spacing-card {
  background:     var(--rest);
  display:        flex;
  flex-direction: column;
}

.logo-spacing-preview {
  flex:            1;
  display:         flex;
  align-items:     center;
  justify-content: center;
  padding:         56px 48px;
  min-height:      320px;
}

.logo-spacing-preview svg { display: block; }

.logo-section-label {
  font-family:     var(--font-ui);
  font-size:       12px;
  font-weight:     300;
  letter-spacing:  0.25em;
  text-transform:  uppercase;
  color:           var(--text-label);
  margin-bottom:   var(--gap-20);
}

.logo-spacing-intro {
  font-family:     var(--font-ui);
  font-size:       16px;
  font-weight:     300;
  color:           var(--text-body);
  line-height:     1.7;
  margin-bottom:   var(--gap-20);
}

.logo-ratio-label {
  font-family:     var(--font-btc);
  font-size:       12px;
  color:           var(--yellow);
  letter-spacing:  0.10em;
}

/* ── Logo specimen — measurement annotations ── */

.logo-measure {
  display:         flex;
  align-items:     center;
  gap:             8px;
  margin-bottom:   16px;
}

.logo-measure-line {
  flex:            1;
  height:          1px;
  background:      var(--yellow);
}

.logo-measure-tick {
  width:           1px;
  height:          6px;
  background:      var(--yellow);
}

/* Fixed-width variant — matches the 200px square/circle container */
.logo-measure--fixed {
  width:           200px;
}

/* Rect card wrapper — forces the stack to full preview width so the
   measure bar and 16:9 container both span edge-to-edge. */
.logo-spacing-stack--full {
  width:           100%;
}

/* ── Logo specimen — container outlines ── */

.logo-container {
  border:          1px dashed var(--yellow);
  display:         flex;
  align-items:     center;
  justify-content: center;
}

.logo-container--rect {
  width:           100%;
  padding-top:     56.25%;
  position:        relative;
}

.logo-container--rect-inner {
  position:        absolute;
  inset:           0;
  display:         flex;
  align-items:     center;
  justify-content: center;
}

.logo-container--square {
  width:           200px;
  height:          200px;
}

.logo-container--circle {
  width:           200px;
  height:          200px;
  border-radius:   50%;
}

/* ── Golden ratio sizing — logo inside each container type ──
   Rect  → 61.8% width (height auto via viewBox aspect ratio)
   Square → 61.8% of container height (width auto)
   Circle → 61.8% of container height (width auto) */
.logo-container--rect-inner .logo { width:  calc(100% / 1.618); height: auto; }
.logo-container--square .logo     { height: calc(100% / 1.618); width:  auto; }
.logo-container--circle .logo     { height: calc(100% / 1.618); width:  auto; }

/* ── Logo specimen — meaning hero ── */

.logo-meaning-hero {
  padding:  80px 0;
  width:    100%;
}

.logo-meaning-hero .logo {
  display:  block;
  width:    calc(100% / 1.618);
  height:   auto;
  margin:   0 auto;
}




/* ============================================================
   12. FIELDS
   Every field on the site is drawn from here. A page may add work
   of its own on top — a label cell, a different row — but it does
   not redraw the field (owner, 16 September 2026).

   THE FOUR STATES OF A FIELD (owner, 16 September 2026). The line,
   the wash and the glow are all the state's own color, the way a
   button keeps its color through every state:
     1 rest      the form gray
     2 typing    blue, with the quiet glow
     3 filled    green — only on a field that CAN be blank
     4 empty     red, required and still blank at Checkout
   Green earns its meaning by being absent sometimes, so a field
   that can never be empty never takes it. The quantity field
   always holds a number, since zero removes the cart line, so it
   has rest, typing and its own caution yellow at the 21 ceiling.

   Anything a visitor types is data and is set in OCR-Bitcoin.
   That font's ink sits high in a box that centers it — 1.5px at
   18 and 21, 1px at 14 — so every box takes a padding-top of
   twice the lift, and centered tracked text takes a text-indent
   of one tracking unit. Website-Aesthetics 4.18.

   HOW TO USE:
   <div class="quantity">
     <button type="button" class="quantity-button is-minus"
             aria-label="One fewer">-</button>
     <input type="text" inputmode="numeric" pattern="[0-9]*"
            class="quantity-field" value="1" aria-label="Quantity">
     <button type="button" class="quantity-button is-plus"
             aria-label="One more">+</button>
   </div>

   Put .is-caution on .quantity at the ceiling. The strokes are
   plain keyboard characters: a hyphen, never U+2212, which this
   font does not carry.

   The mini-cart runs the same field one step down the scale — a
   mini-cart goes down, not up — and keeps its own rules in
   section 6.
============================================================ */

.quantity {
  display:     flex;
  align-items: center;
  gap:         var(--gap-5);
}

/* A text field with a numeric keyboard, as the mini-cart's is, so no
   browser spinner appears */
.quantity-field {
  width:            80px;
  height:           40px;
  min-width:        0;
  padding:          3px 0 0;   /* the OCR-Bitcoin nudge; border-box keeps the box its size */
  border:           1px solid var(--border);
  background-color: var(--rest);
  text-align:       center;
  font-family:      var(--font-btc);
  font-size:        18px;
  letter-spacing:   0.08em;
  text-indent:      0.08em;    /* tracking pulls centered text left by half a space */
  color:            var(--text-label);
  appearance:       textfield;
  -moz-appearance:  textfield;
}

.quantity-field:hover { border-color: var(--blue); }

/* 2 Typing: the blue line and the locked quiet glow in blue */
.quantity-field:focus,
.is-focused .quantity-field {
  outline:      none;
  border-color: var(--blue);
  box-shadow:   0 0 12px 3px rgba(47, 53, 224, 0.2);
}

/* The minus and the plus. Each rests in its own color — the minus red
   because it takes away and at one removes the line, the plus green
   because it adds. They are the only two buttons on the site whose jobs
   are opposites, which is why neither hovers blue. */
.quantity-button {
  position:         relative;
  display:          flex;
  align-items:      center;
  justify-content:  center;
  flex-shrink:      0;
  width:            40px;
  height:           40px;
  padding-top:      3px;   /* the OCR-Bitcoin nudge, as the field takes */
  background-color: transparent;
  border:           1px solid var(--border);
  border-radius:    0;
  font-family:      var(--font-btc);   /* the strokes sit beside a number */
  font-size:        21px;
  line-height:      1;
  cursor:           pointer;
}

.quantity-button.is-minus { color: var(--red); }
.quantity-button.is-plus  { color: var(--green); }

/* On hover the line comes up to the button's own color and the locked
   quiet glow goes behind it — what every color-at-rest button does */
.quantity-button.is-minus:hover:not([disabled]),
.quantity-button.is-minus:active:not([disabled]) {
  background-color: var(--active-red);
  border-color:     var(--red);
  box-shadow:       0 0 12px 3px rgba(224, 48, 48, 0.2);
}

.quantity-button.is-plus:hover:not([disabled]),
.quantity-button.is-plus:active:not([disabled]) {
  background-color: var(--active-green);
  border-color:     var(--green);
  box-shadow:       0 0 12px 3px rgba(50, 180, 67, 0.2);
}

/* At the ceiling the plus goes quiet and the pointer says so. No
   pointer-events:none — that stops the cursor rule reaching it, and a
   disabled button never fires a click anyway. */
.quantity-button[disabled] {
  color:        var(--disabled);
  border-color: var(--disabled);
  cursor:       not-allowed;
}

/* Caution, at the 21 ceiling: the field's line and number go yellow;
   the strokes do not change. Last so it holds under the pointer and
   while clicked in. */
.is-caution .quantity-field {
  border-color: var(--yellow);
  color:        var(--yellow);
}

.is-caution .quantity-field:focus {
  border-color:     var(--yellow);
  background-color: var(--active-yellow);
  box-shadow:       0 0 12px 3px rgba(216, 208, 22, 0.2);
}


/* ── The name field ──
   60 tall like a button, everything in it OCR-Bitcoin: the typed name
   at 18 in label gray, the label inside as the placeholder at fine
   print's 12. The real <label> stays in the markup as .sr-only,
   because a placeholder is not a label to a screen reader. All four
   states, since this field can be blank. */

.name-field {
  width:            100%;
  height:           60px;
  min-width:        0;
  padding:          3px var(--gap-20) 0;   /* the OCR-Bitcoin nudge */
  border:           1px solid var(--border);
  background-color: var(--rest);
  font-family:      var(--font-btc);
  font-size:        18px;
  letter-spacing:   0;
  color:            var(--text-label);
}

.name-field::placeholder {
  font-family:    var(--font-btc);
  font-size:      12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color:          var(--text-label);
  opacity:        1;   /* Firefox dims placeholders by default */
  /* The browser sets a placeholder on the typed text's baseline, so a
     12 on an 18's baseline reads 2.5 low. Padding and line-height
     cannot move a placeholder; a transform can. */
  transform:      translateY(-3px);
}

.name-field:hover { border-color: var(--blue); }

/* 3 Filled: something is in it and nobody is in it */
.name-field:not(:placeholder-shown):not(:focus) {
  border-color: var(--green);
}

/* 2 Typing */
.name-field:focus,
.is-typing .name-field:not(:focus) {
  outline:          none;
  border-color:     var(--blue);
  background-color: var(--active-blue);
  box-shadow:       0 0 12px 3px rgba(47, 53, 224, 0.2);
}

/* 4 Empty at Checkout: the line and the label go red. Last so it holds
   over filled and hover. */
.is-error .name-field {
  border-color: var(--red);
}
.is-error .name-field::placeholder {
  color: var(--red);
}

/* Clicked into while in error: the field follows the typing rules and
   the label goes back to its gray (owner, 16 September 2026). Red says
   "still empty at Checkout"; once the visitor is in it they are typing. */
.is-error .name-field:focus {
  border-color:     var(--blue);
  background-color: var(--active-blue);
  box-shadow:       0 0 12px 3px rgba(47, 53, 224, 0.2);
}
.is-error .name-field:focus::placeholder {
  color: var(--text-label);
}


/* ============================================================
   13. TESTIMONIAL
   Customer / community testimonial card — identity header,
   platform link column with verified-dot, quote body, and
   product footer. Pairs with .omg-grid + .omg-nav pagination
   for multi-card layouts. Data loads from data/testimonials.json
   via script.js.

   HOW TO USE:
   <div class="omg-grid" aria-live="polite">
     <a class="omg-card" href="#">
       <div class="omg-card__header">
         <div class="omg-card__avatar">...</div>
         <div class="omg-card__identity">
           <div class="omg-card__name">Name</div>
           <div class="omg-card__handle">@handle</div>
         </div>
         <a class="omg-card__platform-col" href="...">
           <div class="omg-card__platform">...svg...</div>
           <div class="omg-card__verify">
             <span class="omg-card__verify-dot"></span>VERIFIED
           </div>
         </a>
       </div>
       <div class="omg-card__body">
         <p class="omg-card__quote">"..."</p>
       </div>
       <div class="omg-card__footer">
         <a class="omg-card__product" href="/products/[slug]/">Product Name</a>
       </div>
     </a>
   </div>

   <div class="omg-nav">
     <button class="omg-nav__btn" aria-label="Previous">&lt;&lt;</button>
     <div class="omg-nav__dots">...</div>
     <button class="omg-nav__btn" aria-label="Next">&gt;&gt;</button>
   </div>
============================================================ */

/* ── Grid ── */
.omg-grid {
  display:               grid;
  grid-template-columns: repeat(2, 1fr);
  gap:                   var(--gap-20);
  transition:            opacity 0.5s ease;
}

.omg-grid.omg-grid--fade { opacity: 0; }

/* ── Card ── */
.omg-card {
  position:         relative;
  display:          flex;
  flex-direction:   column;
  border:           none;
  background-color: transparent;
  text-decoration:  none;
  outline:          none;
}

/* ── Header ── */
.omg-card__header {
  height:           var(--testimonial-header);
  background-color: var(--rest);
  display:          flex;
  align-items:      stretch;
  border:           1px solid var(--border);
  flex-shrink:      0;
}

/* ── Avatar ── */
.omg-card__avatar {
  width:           var(--testimonial-header);
  height:          var(--testimonial-header);
  flex-shrink:     0;
  border:          1px solid var(--border);
  margin:          -1px;
  display:         flex;
  align-items:     center;
  justify-content: center;
  padding:         5px;
}

.omg-card__avatar img {
  width:         72px;
  height:        72px;
  border-radius: 50%;
  object-fit:    cover;
  display:       block;
  border:        1px solid var(--border);
}

.omg-card__initials {
  width:           72px;
  height:          72px;
  border-radius:   50%;
  border:          1px solid var(--border);
  display:         flex;
  align-items:     center;
  justify-content: center;
  font-size:       18px;
  font-weight:     600;
  color:           var(--orange);
  background:      var(--disabled);
}

/* ── Identity ── */
.omg-card__identity {
  flex:            1;
  padding:         0 var(--gap-20);
  min-width:       0;
  display:         flex;
  flex-direction:  column;
  justify-content: center;
  gap:             6px;
}

.omg-card__name {
  font-family:    var(--font-ui);
  font-size:      24px;
  font-weight:    600;
  color:          var(--text-body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space:    nowrap;
  overflow:       hidden;
  text-overflow:  ellipsis;
  line-height:    1.2;
}

.omg-card__handle {
  font-family:    var(--font-ui);
  font-size:      14px;
  font-weight:    300;
  color:          var(--text-label);
  letter-spacing: 0.08em;
  white-space:    nowrap;
  overflow:       hidden;
  text-overflow:  ellipsis;
  line-height:    1.2;
}

/* ── Platform column ── */
.omg-card__platform-col {
  width:            96px;
  flex-shrink:      0;
  border:           1px solid var(--placeholder);
  display:          flex;
  margin:           -1px;
  flex-direction:   column;
  text-decoration:  none;
  color:            var(--text-body);
  background-color: var(--overlay-50);
  transition:       background-color 0.5s ease, border-color 0.5s ease, box-shadow 0.5s ease, color 0.5s ease;
}

.omg-card__platform-col:hover {
  background-color: var(--hover);
  border-color:     var(--green);
  box-shadow:       var(--shadow);
  color:            var(--text-body);
}

.omg-card__platform-col:active {
  background-color: var(--active-green);
  border-color:     var(--green);
  box-shadow:       var(--shadow);
  color:            var(--green);
}

/* ── Icon cell ── */
.omg-card__platform {
  flex:            1;
  display:         flex;
  align-items:     center;
  justify-content: center;
}

.omg-card__platform svg {
  width:   36px;
  height:  36px;
  display: block;
  fill:    currentColor;
}

/* ── Verify strip ── */
.omg-card__verify {
  height:          32px;
  flex:            none;
  display:         flex;
  align-items:     center;
  justify-content: center;
  gap:             8px;
  font-size:       10px;
  letter-spacing:  0.16em;
  text-transform:  uppercase;
}

.omg-card__verify-dot {
  width:         6px;
  height:        6px;
  border-radius: 50%;
  color:         var(--green);
  background:    currentColor;
  flex-shrink:   0;
  transition:    box-shadow 0.5s ease;
}

/* Glow is the strong green token */
.omg-card__platform-col:hover .omg-card__verify-dot {
  box-shadow: var(--glow-green-strong);
}

/* ── Body ── */
.omg-card__body {
  height:           var(--testimonial-card);
  background-color: var(--overlay-20);
  flex-shrink:      0;
  overflow-y:       auto;
  scrollbar-width:  thin;
  scrollbar-color:  var(--border) transparent;
}

.omg-card__body::-webkit-scrollbar             { width: 4px; }
.omg-card__body::-webkit-scrollbar-track       { background: transparent; }
.omg-card__body::-webkit-scrollbar-thumb       { background: var(--border); border-radius: 0; }
.omg-card__body::-webkit-scrollbar-thumb:hover { background: var(--text-label); }

.omg-card__quote {
  font-family:    var(--font-ui);
  font-size:      16px;
  font-weight:    400;
  color:          var(--text-label);
  line-height:    1.85;
  letter-spacing: 0.03em;
  padding:        var(--gap-20);
  white-space:    pre-wrap;
}

/* ── Footer ── */
.omg-card__footer {
  height:      60px;
  display:     flex;
  align-items: center;
  border-top:  1px solid var(--border);
  flex-shrink: 0;
  margin-top:  auto;
}

.omg-card__product {
  /* A link since 5 September 2026 (was a button that did nothing).
     display, line-height and text-decoration give the link the
     button's box: 60px tall, 1px borders, text centred on the line. */
  display:          block;
  line-height:      58px;
  text-decoration:  none;
  flex:             1;
  height:           60px;
  padding:          0 16px;
  background-color: var(--rest);
  border:           1px solid var(--border);
  margin:           -1px;
  border-radius:    0;
  color:            var(--text-label);
  font-family:      var(--font-ui);
  font-size:        16px;
  font-weight:      400;
  letter-spacing:   0.18em;
  text-transform:   uppercase;
  text-align:       left;
  cursor:           pointer;
  white-space:      nowrap;
  overflow:         hidden;
  text-overflow:    ellipsis;
  transition-property:        background-color, color, border-color, box-shadow;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

.omg-card__product:hover  { color: var(--orange); background-color: var(--hover); border: 1px solid var(--orange); box-shadow: var(--shadow); }
.omg-card__product:active { background-color: var(--active-orange); box-shadow: var(--shadow); }

/* ── Empty slot ── */
.omg-card--empty {
  border:          1px solid var(--border);
  background:      var(--rest);
  display:         flex;
  align-items:     center;
  justify-content: center;
  min-height:      calc(var(--testimonial-header) + var(--testimonial-card) + 60px);
}

.omg-empty-label {
  font-size:      16px;
  letter-spacing: 0.22em;
  color:          var(--text-label);
  text-transform: uppercase;
}

/* ── Pagination nav ── */
.omg-nav {
  height:          60px;
  display:         none;
  align-items:     center;
  justify-content: space-between;
  gap:             var(--gap-20);
}

.omg-nav__btn {
  width:            96px;
  height:           60px;
  border:           1px solid var(--border);
  background-color: var(--rest);
  color:            var(--text-label);
  font-family:      var(--font-ui);
  font-size:        16px;
  cursor:           pointer;
  display:          flex;
  align-items:      center;
  justify-content:  center;
  flex-shrink:      0;
  transition-property:        background-color, color, border-color, box-shadow;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

.omg-nav__btn:hover {
  color:            var(--blue);
  border-color:     var(--blue);
  background-color: var(--hover);
}

.omg-nav__btn:active {
  color:               var(--blue);
  border-color:        var(--blue);
  background-color:    var(--active-blue);   /* deepens on press */
  box-shadow:          var(--shadow);
  transition-duration: 0s;                   /* instant feedback on click down */
}

.omg-nav__dots { display: flex; align-items: center; gap: 6px; }

.omg-dot {
  width:           21px;
  height:          21px;
  border:          none;
  background:      transparent;
  cursor:          pointer;
  display:         flex;
  align-items:     center;
  justify-content: center;
  padding:         0;
  flex-shrink:     0;
}

.omg-dot::after {
  content:       '';
  display:       block;
  width:         12px;
  height:        12px;
  border-radius: 50%;
  background:    var(--border);
  transition-property:        background-color, box-shadow;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

.omg-dot:hover::after { background-color: var(--blue); }

.omg-dot--active::after {
  background-color: var(--green);
  box-shadow:       var(--glow-green);
}

/* ── State messages ── */
.omg-state {
  grid-column:     1 / -1;
  display:         flex;
  align-items:     center;
  justify-content: center;
  min-height:      280px;
  font-size:       14px;
  letter-spacing:  0.18em;
  text-transform:  uppercase;
  color:           var(--placeholder);
}

.omg-state--error { color: var(--red); }




/* ============================================================
   14. CONTACT
   Single-page layout for /contact/. Built on the .copy-row WRAP +
   ACTION primitive (section 9) — three full-width stacked rows
   (EMAIL · X · NOSTR) read as terminal output / datasheet. The
   npub row is what the stride was designed for; shorter values
   inherit it. Do NOT collapse to a three-column grid to "balance"
   the shorter values — wide rows are the whole point.

   PAGE-SCOPED RULES (body.page-contact):
   - .panel-content > .contact-title gets a tight --gap-5 bottom
     margin so the kicker subhead reads as one header group; the
     full --gap-20 below comes from .contact-channels' margin-top.
   - Panel padding is the global .panel-content rule (section 5,
     LAYOUT); this page used to carry its own copy of it.

   GLOBAL RULES:
   - .contact-title — CRT amber-phosphor glow (3-layer, same recipe
     as .shop-title).
   - .contact-channels / .contact-channels-row / .contact-channel —
     vertical flex stacks. Single column at every viewport.
============================================================ */

body.page-contact .panel-content > .contact-title { margin-bottom: var(--gap-5); }

/* .contact-title carries no glow of its own since 6 September 2026: the
   element also has .text-display1-144px, which carries recipe 04. */

.contact-channels {
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-20);
  margin-top:     var(--gap-20);
}

.contact-channel {
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-10);
  min-width:      0;
}

.contact-channels-row {
  display:        flex;
  flex-direction: column;
  gap:            var(--gap-20);
}
.contact-channels-row > .contact-channel { min-width: 0; }




/* ============================================================
   15. CARDS
   The product card, locked 20 September 2026 (owner). A spec sheet,
   not a retail tile: the first card was scrapped for its
   presentation after the owner found the shop page's placeholder
   closer to the brand. Every round is in git between e8e6e42 and
   5d1435e, in .dev/05-components/cards.css, now deleted.

   THE CARD, top to bottom: the photo, mounted 20 in with a line of
   its own; the name, .text-h3-21px, with a line under it; the
   price, two ledger rows, USD and SATS. Three across the column,
   20 between. The whole card is one link and opens the product page
   in every stock state — a sold-out card is still readable, still
   clickable.

   THE HOVER, one state, the pointer anywhere on the card: the
   card's line and the photo's frame take the theme color, the
   card's ground goes to the mini-cart's black, the quiet glow sits
   round the card, the name takes the small-title glow, the page's
   own scanlines fade in over the photo, and the photo grows four
   percent inside its still frame. Every fade is the site's half
   second. Section 4's reduced-motion rule stops all of it.

   THE THEME is blue, or the stock state's own color: .card-sold-out
   red, .card-restocking yellow, .card-coming-soon green. At rest a
   state card is the regular card. Its word rides the photo as a tag
   at rest and on hover, because a phone has no hover.

   NO DOT LEADER on a card (owner, 20 September 2026). The footer's
   ledger has one and keeps it; here the label holds the left and the
   figure the right, and the row reads cleaner for it. The leader was
   tried in --text-label and again in --border before it came off.

   HOW TO USE:
   <div class="card-grid">
     <article class="card">            (plus .card-sold-out etc)
       <a class="card-link" href="/products/<slug>/">
         <div class="card-picture">
           <img class="card-photo" src="..." alt="...">
           <span class="card-state-tag" aria-hidden="true">SOLD-OUT</span>
                                     (or RESTOCK, or COMING-SOON)
         </div>
         <div class="card-name text-h3-21px">Name</div>
         <div class="card-price">
           <span class="card-price-row">
             <span class="card-spec-label text-data-14px">USD</span>
             <span class="card-dollars">$49.99</span>
           </span>
           <span class="card-price-row">
             <span class="card-spec-label text-data-14px">SATS</span>
             <span class="card-sats" data-dollars="49.99" hidden></span>
           </span>
         </div>
       </a>
     </article>
   </div>

   The stock word is typed in capitals, plain keyboard characters
   only: OCR-Bitcoin carries no others. script.js fills .card-sats
   from data-dollars when the live rate lands.
============================================================ */

/* ── Three cards across the column, 20 between them (owner). The
   two narrower bands are in section 16. ── */
.card-grid {
  display:               grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap:                   var(--gap-20);
  /* The cards of a row end level, and the price stands on the floor
     of each, so the prices line up across the row */
  align-items:           stretch;
}

/* ── The card. Its theme is blue, or its stock state's color. ── */
.card {
  --card-theme:      var(--card-state-color, var(--blue));
  --card-theme-glow: var(--card-state-glow, 0 0 12px 3px rgba(47, 53, 224, 0.2));

  position:               relative;
  display:                grid;
  grid-template-columns:  minmax(0, 1fr);
  grid-template-areas:    "picture" "name" "price";
  /* The last row takes the spare height and the price sits on its
     floor, so a long name never pushes a card's price out of line */
  grid-template-rows:     auto auto 1fr;
  min-width:              0;
  padding-bottom:         var(--gap-15);
  border:                 1px solid var(--border);
  /* The ground in two layers, as the mini-cart's is: a color, with
     the buttons' own 3% cream laid over it. At rest the color is
     clear, so the page's ground shows through the cream and the card
     rests on --rest exactly. On hover the color goes to black, which
     is the mini-cart's ground; a color fades where a gradient cannot. */
  background-color:           transparent;
  background-image:           linear-gradient(var(--rest), var(--rest));
  transition-property:        border-color, box-shadow, background-color;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

/* The link is the whole card. It dissolves into the grid, so its
   parts are the grid's. */
.card-link {
  display:         contents;
  color:           inherit;
  text-decoration: none;
  outline:         none;
}

.card-picture { grid-area: picture; }
.card-name    { grid-area: name; }
.card-price   { grid-area: price; align-self: end; }

/* THE HOVER: the card's line in its theme, the quiet glow, the
   mini-cart's ground */
.card:is(:hover, :has(:focus-visible)) {
  border-color:     var(--card-theme);
  box-shadow:       var(--card-theme-glow);
  background-color: var(--black);
}

/* The keyboard's mark, on the card since the link has no box */
.card:has(.card-link:focus-visible) {
  outline:        1px solid var(--orange);
  outline-offset: -3px;
}

/* ── The photo: square, mounted 20 in with a line of its own. The
   frame holds still while the photo grows inside it on hover; the
   clip is at the padding box, so the photo never covers the line. ── */
.card-picture {
  position:                   relative;
  display:                    block;
  margin:                     var(--gap-20) var(--gap-20) 0;
  border:                     1px solid var(--border);
  overflow:                   hidden;
  background-color:           var(--bg);
  transition-property:        border-color;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

.card:is(:hover, :has(:focus-visible)) .card-picture { border-color: var(--card-theme); }

.card-photo {
  display:                    block;
  width:                      100%;
  aspect-ratio:               1;
  object-fit:                 cover;
  transition-property:        transform;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

/* The photo grows four percent on hover (owner); two and six were
   the other options. Transform and opacity are the two properties
   the browser hands to the graphics card, so this and the scanlines
   together cost one hover about two milliseconds of work
   (.dev/scripts/check-card-motion.js). */
.card:is(:hover, :has(:focus-visible)) .card-photo { transform: scale(1.04); }

/* The scanlines over the photo on hover (owner): the recipe of the
   odd panels' own scanlines in section 5, drawn dark so it reads on
   a photo. A box over the photo and under the stock tag, clear at
   rest, so the photo's own file is never touched. */
.card-picture::after {
  content:                    "";
  position:                   absolute;
  inset:                      0;
  pointer-events:             none;
  opacity:                    0;
  background-image:           repeating-linear-gradient(
                                to bottom,
                                transparent 0 2px,
                                var(--overlay-50) 2px 3px
                              );
  transition-property:        opacity;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

.card:is(:hover, :has(:focus-visible)) .card-picture::after { opacity: 1; }

/* ── The stock word: a tag on the photo, 30 tall, 10 each side, 20 in
   from its bottom left, no line, on the 70% black ground so it reads
   on any photo. Shown at rest and on hover, since a phone has no
   hover. Only a card in a stock state carries one. ── */
.card-state-tag {
  position:         absolute;
  left:             var(--gap-20);
  bottom:           var(--gap-20);
  z-index:          var(--z-active);
  display:          flex;
  align-items:      center;
  height:           30px;
  /* The OCR-Bitcoin nudge at 14: twice the 1px lift */
  padding:          2px var(--gap-10) 0;
  background-color: var(--overlay-70);
  font-family:      var(--font-btc);
  font-size:        14px;
  line-height:      1;
  letter-spacing:   0.12em;
  /* One tracking unit, so the word sits centered between the sides */
  text-indent:      0.12em;
  color:            var(--card-state-color);
}

/* ── The name: 20 in, a line under it, the price below. The
   small-title glow on hover only (owner). ── */
.card-name {
  margin:                     var(--gap-15) var(--gap-20) 0;
  padding-bottom:             var(--gap-15);
  border-bottom:              1px solid var(--border);
  overflow-wrap:              anywhere;
  transition-property:        text-shadow;
  transition-duration:        0.5s;
  transition-timing-function: ease;
}

.card-name.text-h3-21px { text-shadow: none; }

/* The small-title glow, recipe 05 */
.card:is(:hover, :has(:focus-visible)) .card-name.text-h3-21px {
  text-shadow:
    0 0 1px  rgba(245, 243, 239, 1),
    0 0 6px  rgba(227, 121, 27, 0.8),
    0 0 20px rgba(227, 121, 27, 0.35);
}

/* ── The price: two ledger rows, USD and SATS, each a label, a dot
   leader and a figure. ── */
.card-price { margin: var(--gap-10) var(--gap-20) 0; }

/* The label holds the left, the figure the right. No dot leader on a
   card (owner, 20 September 2026): tried in --text-label and in
   --border, and the card reads better without it. The footer's
   ledger keeps its own. */
.card-price-row {
  display:         flex;
  align-items:     baseline;
  justify-content: space-between;
  gap:             var(--gap-10);
}

/* No sats yet, no row for them */
.card-price-row:has(.card-sats[hidden]) { display: none; }

/* The label: 18 of line and the OCR-Bitcoin nudge at 14, twice the
   1px lift, make a row of 20 */
.card-spec-label.text-data-14px {
  padding-top: 2px;
  line-height: 18px;
  white-space: nowrap;
  color:       var(--text-label);
}

/* The dollars: a row of 20, as the label */
.card-dollars {
  padding-top:    2px;
  font-family:    var(--font-btc);
  font-size:      14px;
  line-height:    18px;
  letter-spacing: 0.12em;
  color:          var(--green);
}

/* The sats: a row of 30, 27 of line and the nudge at 21, twice the
   1.5px lift */
.card-sats {
  padding-top:    3px;
  font-family:    var(--font-btc);
  font-size:      21px;
  line-height:    27px;
  letter-spacing: 0.06em;
  color:          var(--text-body);
}

/* The sats wait hidden for the live rate; the dollars are there
   either way, so the price never goes blank */
.card-sats[hidden] { display: none; }

/* ── The stock states. The class on the card sets the state's color
   and its quiet glow; the card's theme reads them. ── */
.card-sold-out {
  --card-state-color: var(--red);
  --card-state-glow:  0 0 12px 3px rgba(224, 48, 48, 0.2);
}

.card-restocking {
  --card-state-color: var(--yellow);
  --card-state-glow:  0 0 12px 3px rgba(216, 208, 22, 0.2);
}

.card-coming-soon {
  --card-state-color: var(--green);
  --card-state-glow:  0 0 12px 3px rgba(50, 180, 67, 0.2);
}




/* ============================================================
   16. RESPONSIVE

   ── The four bands, decided 12 September 2026 ──
   Every edge is a reading off the footer, not a convention:

     desktop   1440 and up      the content caps at 1200, so nothing
                                moves above 1240
     laptop    1080 to 1439     the desktop footer, 30 each side of
                                the rule instead of 60
     tablet     768 to 1079     the halves and the rule kept, the
                                link columns folded, the bottom row
                                centred
     phone      341 to  767     the footer stacks

   Why 1080 rather than the usual 1024. Two things have to fit and
   the larger wins. The halves need 1072 with the 60 gutter and
   1012 with 30, so they alone would reach 1024. But the bottom row
   is a 1fr auto 1fr grid whose outer columns are forced equal, so
   the 407 legal run and the 190 block credit both take 407: the
   row needs 1030 of content, which is 1070 of screen. Below that
   the socials leave the centre — measured 23.2 off at 1024, and
   that reading set the edge.

   Why 768. Each half has 348 of content there and the three link
   columns in a row need 456, so that is where the halves stop
   holding them and the columns fold.

   ── The header's own two edges, kept 12 September 2026 ──
   The header does not change on the band edges above, and that is
   deliberate. The owner looked at how it moves across the four bands
   and kept it (12 September 2026). Its edges are set by its own
   contents rather than by the footer's:

     900px  — the top nav hides, the hamburger appears, and the
              mini-cart goes fullscreen. The nav is six links; below
              about 900 they cannot sit beside the wordmark and the
              cart without crowding, and the 560-wide popover hangs
              off the same edge, so it wants to go when the header
              does. This sits inside the tablet band on purpose: the
              footer folds at 1079 because its halves run out of room
              there, and the header has room for 179 more.
     480px  — the header cart goes and the hamburger's menu carries
              the Cart link. At about 456 the cart trigger's total
              starts to clip, so 480 is the last round number above
              the failure. Inside the phone band, for the same reason.

   Between 900 and 480 the menu button and the cart are both 46 high
   (owner, 12 September 2026) — the pair is the only thing about the
   header that was changed in this pass.

   One older breakpoint, now settled against the four bands:

     1200px — was the side nav's hiding point and the footer's stack
              point. Neither any more: the strip goes at 1079 with the
              laptop band, and 1200 is only the top of the laptop
              band's gutter rule above.

   Add new responsive overrides to the matching block.
   Rule: desktop first — only put rules here that CHANGE something
   from the desktop version. Don't repeat desktop styles.
============================================================ */

/* ── Side nav hidden below the laptop band ──
   The strip follows the footer down to 1080 (owner, 12 September
   2026); it used to go at 1200, which was the footer's old stack
   point. It is fixed 20 in from the right edge and 40 wide, so it
   covers the 60 nearest that edge, and the content column has to be
   told to stop short of that — see the laptop band below. */
@media (max-width: 1079px) {
  .nav-side { display: none; }
}


/* ── LAPTOP, 1080 to 1200 ──
   The desktop footer, whole. Its one change is 30 each side of the
   rule instead of 60, which took what the halves need from 1072 to
   1012 (measured with the column titles at 14) and so carried the
   desktop shape down to 1080. With the titles at 18 the columns fold
   too, below. Above 1200 the
   content is capped at 1200 and the 60 has room again. */
@media (min-width: 1080px) and (max-width: 1200px) {
  .footer-half-links { padding-right: var(--gap-30); }
  .footer-half-shop  { padding-left:  var(--gap-30); }

  /* The column titles went from 14 to 18 (owner, 18 September 2026)
     and three across now needs 537; the half has 540 at the top of
     the band and 480 at the bottom, where Proof-of-Work ran 17 into
     the rule. So the tablet's fold comes up into this band: Index
     and Support across, Proof-of-Work under them. */
  .footer-cols {
    display:               grid;
    grid-template-columns: repeat(2, max-content);
    column-gap:            var(--gap-30);
    row-gap:               var(--gap-30);
  }

  .footer-col:nth-child(3) { grid-column: 1 / -1; }

  /* The content stops short of the side nav. The strip is fixed 20 in
     from the right edge and 40 wide, so it owns the 60 nearest that
     edge; 120 off the width, taken off both sides, keeps the column
     centred and leaves it 20 clear of the strip at every width in the
     band. Above 1200 the 1080 cap does the same job on its own —
     1200 less 120 is 1080, so the two meet with no step.

     This is why the strip used to go at 1200. Measured on the home,
     shop and contact pages: the column touches the strip at 1160 and
     runs 40 under it at 1080, so following the footer down to 1080
     (owner, 12 September 2026) costs the content 120 of width in this
     band and nothing anywhere else. The product page keeps its own
     copy of this rule in products/lightning-keychain/index.css, since
     that page is built on .pd-wrap rather than on a panel. */
  .panel-content { max-width: calc(100% - 120px); }
}


/* ── TABLET, 768 to 1079 ──
   The halves and the rule stay. What changes is inside them: the
   gutter closes to 20, the link columns fold, the ledger tightens,
   and the bottom row centres. */
@media (min-width: 768px) and (max-width: 1079px) {

  /* Two cards across: three in a column this narrow leave a photo
     under 200, and the sats figure starts clipping its leader. */
  .card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .footer-half-links { padding-right: var(--gap-20); }
  .footer-half-shop  { padding-left:  var(--gap-20); }

  /* The name is sized off the viewport, clamp(36px, 8vw, 60px), but
     here it has only half of it to sit in. At 768 that put it at its
     60 ceiling, 408 wide in a half of 348, and MANUFACTURING ran
     through the rule and across the ledger. Half the room, so half
     the coefficient: 36 up to 900, 43 at the top of the band. The
     floor is display 3's own, a step on the type scale (owner, 14
     September 2026). */
  .footer-wordmark { font-size: clamp(36px, 4vw, 60px); }

  /* Index and Support across, Proof-of-Work spanning the row under
     them (T1 on .dev/01-background/footer.html, 12 September 2026).
     Three in a row needs 456 and the half has 348; dropping the tree
     marks to make the room saves 45 and it needs 108, so three across
     cannot exist below about 940 however it is drawn. */
  .footer-cols {
    display:               grid;
    grid-template-columns: repeat(2, max-content);
    column-gap:            var(--gap-30);
    row-gap:               var(--gap-30);
  }

  .footer-col:nth-child(3) { grid-column: 1 / -1; }

  /* The ledger closes up: the rows from 20 to 10, and the leader
     gives up its minimum, since it is a run of dots and has nothing
     of its own to keep. */
  .footer-ledger-row    { gap: var(--gap-10); }
  .footer-ledger-leader { min-width: 0; }

  /* The block row's number is far bigger than the two under it, so its
     leader is squeezed to a single dot here, which reads as a mistake
     rather than as a run. Only the dots go: the span stays, because it
     is the flex item that takes up the slack, and hiding the element
     outright left the number sitting against its label on the left
     instead of on the right edge with the two prices (owner, 12
     September 2026). The two price rows keep their dots. */
  .footer-ledger-block .footer-ledger-leader::before { content: none; }

  /* The number itself follows the half, which is half the screen, so
     half the phone's coefficient: 30 at 768, 36 from about 845 up.
     The floor of 24 is what the nine glyphs of 1,000,000 need. */
  .footer-ledger-block .footer-tile-value {
    font-size: clamp(24px, calc(7.9vw - 30.5px), 36px);
  }

  /* The bottom row centres into one column. Legal left, socials
     centre, block right needs 1070 of screen before the socials come
     off the centre line, and this band never has it. The one place
     the tablet is not the desktop, and it is fine print. */
  .footer-bottom {
    grid-template-columns: minmax(0, 1fr);
    justify-items:         center;
    row-gap:               var(--gap-20);
  }

  .footer-bottom .footer-legal      { justify-self: center; }
  .footer-bottom .footer-foundation { justify-self: center; text-align: center; }

  .footer-legal {
    flex-wrap:       wrap;
    justify-content: center;
    gap:             var(--gap-10);
  }
}


/* ── PHONE, 341 to 767 ──
   The footer stacks: reading first, then buying. Settled 12
   September 2026 from P1 on .dev/01-background/footer.html. */
@media (max-width: 767px) {

  /* One card across. No hover exists here, so the scanlines and the
     photo's growth never run; the stock tag shows at rest for that
     reason. */
  .card-grid { grid-template-columns: minmax(0, 1fr); }

  /* Running copy goes back to one column. Held, not picked (owner,
     15 September 2026): two halves of a 350-wide screen are 165
     each, fifteen characters a line. The column width is cleared as
     well as the count, or the named width would keep choosing. */
  .column-text {
    column-width: auto;
    column-count: 1;
  }

  .footer-inner { padding: 60px var(--gap-20) 40px; }

  /* The halves one above the other. A column, not a grid, so the air
     between them can be written on the halves themselves and the rule
     can carry its own 30 each side. */
  .footer-main {
    display:        flex;
    flex-direction: column;
    gap:            0;
  }

  .footer-half-links { padding-right: 0; }

  /* The rule turns. It was the right half's left edge; stacked, it is
     that half's top edge, with 30 above and 30 below — the gap scale's
     inside step twice, which makes the between-blocks 60 with the line
     in the middle of it. */
  .footer-half-shop {
    padding-left: 0;
    border-left:  0;
    margin-top:   var(--gap-30);
    padding-top:  var(--gap-30);
    border-top:   1px solid var(--border);
  }

  /* One column to a row, 40 apart (owner, 12 September 2026). Two
     columns of three links each read as a grid of six on a phone, and
     the three groups want telling apart. */
  .footer-cols {
    flex-direction: column;
    gap:            var(--gap-40);
  }

  /* The leader gives up its minimum so the number always has its room */
  .footer-ledger-leader { min-width: 0; }

  /* The block number is 36 down to 421 wide and then follows the
     screen, so the nine glyphs of 1,000,000 still fit on one line
     beside the label at 341: 31 at 390, 24 at 341. */
  .footer-ledger-block .footer-tile-value {
    font-size: clamp(24px, calc(15.8vw - 30.5px), 36px);
  }

  /* The bottom row, one centred column: the legal links, the socials,
     the Foundation Block, 20 apart. */
  .footer-bottom {
    grid-template-columns: minmax(0, 1fr);
    justify-items:         center;
    row-gap:               var(--gap-20);
  }

  .footer-bottom .footer-legal { justify-self: center; }

  .footer-bottom .footer-foundation {
    justify-self: center;
    white-space:  normal;
    text-align:   center;
  }

  /* The three legal links need 407 and a phone gives 358 at most, and
     a wrapped row left a // dangling at the end of the first line. One
     under another instead, 10 apart, the separators gone with the line
     they marked. */
  .footer-legal {
    flex-direction: column;
    align-items:    center;
    gap:            var(--gap-10);
  }

  .footer-legal-sep { display: none; }
}


@media (max-width: 900px) {

  /* Type scale handled by clamp() on the base classes themselves
     (see .text-display1-144px through .text-h1-36px). No breakpoint
     overrides needed — clamp keeps things smooth across this rule. */

  /* Shop grid collapses to 2 columns on tablet */
  .shop-grid { grid-template-columns: repeat(2, 1fr); }

  /* ── Logo color specimen collapses to 2 columns on mobile ── */
  .logo-colors { grid-template-columns: repeat(2, 1fr); }

  /* ── Testimonial grid collapses to a single column on mobile ── */
  .omg-grid { grid-template-columns: 1fr; }

  /* ── Accordion full width on mobile ── */
  .acc { width: 100%; }

  /* ── Header: nav hides, hamburger appears, cart trigger shrinks ── */
  .header-nav            { display: none; }
  .header-menu-trigger   { display: flex; margin: 0 var(--gap-20); }
  .header-cart           { font-size: 16px; padding: 0 var(--gap-20); }

  /* The two boxed controls are the same height (owner, 12 September
     2026). They were not: measured by .dev/scripts/check-header.js at
     900 and 768, the menu button was 46 — a 24 icon, 10 of padding
     each side, 1 of border — and the cart 39, 16px text on a 21 line
     with 8 of padding. 46 is the number, so the menu button does not
     move and only the cart grows, and it clears the 44 a tap target
     wants. Written as a height rather than as padding so the two
     cannot drift apart again when either one's type changes. Below
     480 the cart goes and the hamburger's menu carries the Cart link,
     so there is no pair left to match. */
  .header-menu-trigger,
  .header-cart {
    height:         46px;
    padding-top:    0;
    padding-bottom: 0;
  }

  /* Mini-cart goes fullscreen — sits below header z-wise so the
     trigger stays tappable for "open one closes the other" swap.
     overflow-y:auto lets content scroll when it exceeds 100vh (zoom
     ≥200%, accessibility text scaling, small phones). overscroll-behavior
     contains rubber-band scrolling so it doesn't bubble out to the page. */
  .mini-cart {
    position:            fixed;
    top:                 0;
    left:                0;
    right:               auto;          /* clear base "right: max(...)" */
    width:               100%;
    height:              100vh;
    height:              100dvh;
    padding:             calc(var(--header-height) + var(--gap-20)) var(--gap-20) var(--gap-20);
    border:              none;
    box-shadow:          none;
    overflow-y:          auto;
    overscroll-behavior: contain;
    z-index:             80;
  }

  /* Fullscreen, so the panel itself scrolls and the list is not capped.
     No inner scrollbar here, so the room the desktop list leaves for one
     goes back to the content. */
  .mini-cart-list {
    max-height: none;
    overflow-y: visible;
  }

  /* The scrollbar room lives on the item now, so it comes off there */
  .mini-cart-item { padding-right: 0; }

  /* No scrollbar to clear, so the subtotal meets the same edge as the lines */
  .mini-cart-subtotal { padding-right: 0; }

  /* Nothing about the footer here. It has four bands of its own above,
     and 900 is not one of their edges. */

}


/* ── Narrow-phone breakpoint ──
   At ~456px the header cart trigger ($0.00 + icon) starts clipping; below
   480px we drop the trigger entirely. Cart access shifts to the hamburger
   menu's "Cart" link (added in template.html and /cart/index.html). The
   mobile menu drawer remains the full nav surface, so no functionality
   is lost — users tap the hamburger, then "Cart". */
@media (max-width: 480px) {
  .header-cart-wrap { display: none; }

  /* Shop grid stacks to single column on smallest phones */
  .shop-grid { grid-template-columns: 1fr; }
}
