/* ============================================================
   rawcloser.com — Components (Phase 7.A.1)
   Only components every page in 7.A shares: nav, footer, buttons,
   card, section wrapper. Nothing speculative — no component added
   here that no page uses yet.
   Consumes tokens.css only. Never hardcode a color, spacing,
   radius, type, shadow, or motion value in this file.
   ============================================================ */

/* ----- Section wrapper -----
   Every page section uses this for fluid vertical rhythm; a nested
   .container centers content and caps its width. */
.section{
  padding-block:var(--space-section-y);
}

.container{
  max-width:var(--container-max);
  margin-inline:auto;
  padding-inline:var(--container-pad-x);
}

/* ----- Nav -----
   Sticky, glass surface (matches the app's glass tokens). Holds the
   logo lockup, primary links, and the one nav CTA. No theme toggle —
   the site follows the OS theme only (Decisions.md §Website, LOCKED
   2026-07-19).

   The <header> is position:FIXED (7.A.12), a sibling of the damped-scroll
   .smooth wrapper so its transform never touches the bar — the nav stays pinned
   to the viewport top while the content scrolls (and lags) beneath. NOT
   position:sticky: the new hide-on-scroll-down behaviour drifts and fades the
   whole header, which a fixed element expresses cleanly; sticky was also removed
   because it reserved layout height the transparent bar no longer needs. .nav is
   a plain flex bar inside this fixed header. NOTE (7.A.13): the section progress
   line is NO LONGER inside this header — it is now its own independent
   position:fixed pill (landing.css .progress), so it does NOT fade/drift away
   when this header hides on scroll-down; it stays visible on its own.

   HIDE / SHOW (7.A.12): past the first section nav.js toggles .is-nav-hidden by
   scroll direction. The bar FADES (opacity) and DRIFTS slightly up (transform),
   unhurried, on the shared --dur-nav / --ease-gentle motion so it reads as
   drifting away and returning rather than snapping. --dur-nav is zeroed under
   prefers-reduced-motion (base.css); nav.js also never hides the bar under
   reduced motion, so it simply stays visible there. pointer-events:none on the
   hidden state keeps the invisible bar from swallowing clicks. */
header{
  position:fixed;
  top:0;
  left:0;
  right:0;
  z-index:var(--z-nav);
  transition:opacity var(--dur-nav) var(--ease-gentle),
             transform var(--dur-nav) var(--ease-gentle);
}

/* Hidden state (scrolled down, past the first section). The fade does the
   hiding; the small upward drift is the accent. */
header.is-nav-hidden{
  opacity:0;
  transform:translateY(-0.75rem);
  pointer-events:none;
}

.nav{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--space-4);
  padding-block:var(--space-3);
  padding-inline:var(--container-pad-x);
  /* Fully TRANSPARENT bar (7.A.12): no background fill, no border, no
     backdrop blur. The glass treatment moved to the individual nav links on
     hover/focus (see .nav-links a below). The nav text now sits directly over
     page content, so its colour must stay legible in both themes over every
     section it can appear above (all section fills are theme-adaptive tokens,
     so nav text and backgrounds move together — see landing.css). */
  background:transparent;
}

/* Compact SIZE state (past the first section). Everything scales down together
   so the whole bar reads uniformly smaller: vertical padding (--space-3 ->
   --space-1), the logo, the links, and the CTA button (all below). This is a
   STATE CHANGE, not a morph — none of the compacting properties carries a
   transition (the old --dur-nav resize tween was deleted in 7.A.12), so
   full<->compact switches instantly at the section-1 boundary. Only the
   header's hide/show fade+drift is animated now. */
.nav--compact{
  padding-block:var(--space-1);
}

/* Right-hand cluster: links + the one nav CTA. Held apart from the
   brand by the .nav's space-between. Added 7.A.2. */
.nav-actions{
  display:flex;
  align-items:center;
  /* Gap between the links cluster and the CTA opened --space-4 ->
     --space-5 so the button breathes away from the links (7.A.2 polish
     change 4). */
  gap:var(--space-5);
}

.nav-links{
  display:flex;
  align-items:center;
  /* Inter-link gap opened --space-5 -> --space-6 so Features / Pricing
     are not cramped (7.A.2 polish change 4). */
  gap:var(--space-6);
}

.nav-links a{
  /* Bumped --text-sm -> --text-base (7.A.2 polish change 4): the links
     read as fine print at 13px. --text-base matches the nav CTA's text
     size; both links share this one selector, so they are guaranteed the
     same size as each other. */
  font-size:var(--text-base);
  font-weight:var(--weight-medium);
  color:var(--color-text-secondary);
  /* Hover/focus pill geometry (7.A.12): each link is a pill-shaped button
     target — the blurred translucent surface appears here on hover/focus,
     since the bar itself is now transparent. Padding gives the pill body;
     --radius-pill matches the CTA button and every other pill on the site. */
  padding-block:var(--space-1);
  padding-inline:var(--space-3);
  border-radius:var(--radius-pill);
  background:transparent;
  /* No more font-size morph (deleted with the compact tween in 7.A.12). Colour
     and the pill background both fade smoothly; the pill uses --dur-base so it
     eases in/out rather than snapping. Both zero under reduced motion. */
  transition:color var(--dur-fast) var(--ease-standard),
             background var(--dur-base) var(--ease-standard);
}

/* Blur pill on the HOVERED / keyboard-FOCUSED item only — the button-shaped
   background sits behind THAT link alone. Uses the site's glass surface + the
   softened 10px blur (visual-effect value, 7.A.1 latitude). focus-visible gets
   the identical treatment so keyboard users get the same affordance — it is
   deliberately not hover-only. Nothing else in the bar gains a background. */
.nav-links a:hover,
.nav-links a:focus-visible{
  color:var(--color-text);
  background:var(--glass-bg);
  backdrop-filter:blur(10px);
  -webkit-backdrop-filter:blur(10px);
}

/* Compact nav steps the links down one type size (--text-base -> --text-sm)
   so they scale with the logo, not just sit beside it. */
.nav--compact .nav-links a{
  font-size:var(--text-sm);
}

/* ----- Brand logo lockup (nav + footer) — added 7.A.2 -----
   The full RawCloser lockup (checkmark + wordmark + underline-arrow),
   swapped by [data-theme]: logo-light on the light site, logo-dark on
   the dark site. Two <img>s with CSS showing the matching one, because
   [data-theme] can't be expressed inside a single <img>/<source>. No
   tagline in the lockup. The 5:1 asset renders ~160px wide at this
   height. */
.brand{
  display:inline-flex;
  align-items:center;
}

.brand-logo{
  height:var(--space-6);
  width:auto;
  /* No height morph (7.A.12): the compact size is a state change, not a tween,
     so the logo snaps between full and compact with the rest of the bar. */
}

/* Compact nav shrinks the logo one space step (--space-6 -> --space-5)
   so the smaller bar stays balanced. Instant state change, no morph. */
.nav--compact .brand-logo{
  height:var(--space-5);
}

/* Compact nav shrinks the CTA proportionally: one type step down
   (--text-base -> --text-sm) and tighter padding (block --space-2 ->
   --space-1, inline --space-5 -> --space-4). The pill radius and the
   button's identity are untouched — it just gets smaller. Only the nav's
   button lives inside .nav--compact, so the hero CTA is never affected.
   Instant state change (7.A.12): the .btn size-morph transition was removed,
   so the CTA snaps between full and compact with the rest of the bar. */
.nav--compact .btn{
  font-size:var(--text-sm);
  padding-block:var(--space-1);
  padding-inline:var(--space-4);
}

.brand-logo--dark{
  display:none;
}

[data-theme="dark"] .brand-logo--light{
  display:none;
}

[data-theme="dark"] .brand-logo--dark{
  display:block;
}

/* ----- Mobile nav dropdown, Phase 7.A.32 -----
   Below 60rem the nav had NO mobile rule at all before 7.A.31: .nav-links and
   the .btn CTA kept their full desktop sizing (font-size, padding and gaps
   all this file's --text-* /--space-* tokens, which tokens.css defines as
   literal PX — 15px, 24px, and so on, never rem or vw) at any viewport width,
   so three desktop-sized items plus the logo simply do not fit a phone-width
   bar — that is the overflow, not a unit that scales with anything. Two
   separate fixes below: the plain items are sized down a step so the no-JS
   fallback still fits, and above that a JS-enhanced phone additionally swaps
   them for one button opening a small dropdown panel with the same three —
   REPLACING 7.A.31's full-height right-side drawer (too heavy for three
   links; see .nav-dropdown below, after the ----- Modal ----- section, for
   the panel itself and why it does not reuse .modal-backdrop/.modal-dialog). */

/* Anchor for the button + its dropdown panel: position:relative so the panel
   (below, after ----- Modal -----) can position itself against THIS box's own
   footprint — the button, not the wider .nav-actions cluster or the full nav
   bar — so "anchored under the button" holds regardless of how much room the
   links/CTA take on a given page. inline-flex so it takes no space of its own
   beyond its children (both display:none until nav-js-ready), causing no
   layout shift. */
.nav-menu{
  position:relative;
  display:inline-flex;
}

/* Real button (not a div) so it is keyboard-operable and announced by
   assistive tech unassisted; aria-expanded/aria-controls are in the markup,
   nav-menu.js keeps aria-expanded synced to the panel's open state. Ships
   display:none — revealed only inside the mobile block below, and even
   there only once nav-js-ready proves the panel actually works (see
   nav-menu.js). Sized off --space-7 (48px, a literal px token — see the
   file-header note above) rather than a rem box like .modal-close's: a
   px box does not grow when the visitor's root font-size / browser text-
   scaling setting does, so the tap target and its position in the bar stay
   fixed regardless of that setting — the exact thing left unfixed above. */
.nav-menu-btn{
  display:none;
  align-items:center;
  justify-content:center;
  width:var(--space-7);
  height:var(--space-7);
  padding:0;
  border:0;
  border-radius:var(--radius-pill);
  background:transparent;
  color:var(--color-text);
  cursor:pointer;
  transition:background var(--dur-base) var(--ease-standard);
}

.nav-menu-btn:hover,
.nav-menu-btn:focus-visible{
  background:var(--glass-bg);
}

.nav-menu-btn svg{
  width:var(--space-5);      /* 24px, same px-token reasoning as the button box */
  height:var(--space-5);
  display:block;
}

@media (max-width: 59.999rem){
  /* No-JS / not-yet-wired baseline: the three items stay real, visible,
     working links — just a step smaller so they fit a phone-width bar next
     to the logo instead of overflowing it. */
  .nav-links{
    gap:var(--space-3);
  }
  .nav-links a{
    font-size:var(--text-sm);
    padding-inline:var(--space-2);
  }
  .nav-actions{
    gap:var(--space-3);
  }
  .nav-actions .btn-primary{
    font-size:var(--text-sm);
    padding-inline:var(--space-4);
  }

  /* JS-enhanced: nav-menu.js adds .nav-js-ready to .nav only once it has a
     working dropdown, so the swap below never leaves a visitor stranded. */
  .nav.nav-js-ready .nav-links,
  .nav.nav-js-ready .nav-actions > .btn-primary{
    display:none;
  }
  .nav.nav-js-ready .nav-menu-btn{
    display:inline-flex;
  }
}

/* The dropdown panel's own styling sits AFTER the ----- Modal ----- section
   below, not inline here, purely to keep this file's read order (nav, then
   modal, then footer) — unlike 7.A.31's drawer, .nav-dropdown does NOT
   override or reuse .modal-backdrop/.modal-dialog at all (see its own
   comment for why: modal.js's centred-dialog contract does not fit a small
   anchored panel), so there is no cascade-order dependency forcing it there
   the way the drawer had. See .nav-dropdown just after that section. */

/* ----- Footer -----
   The real <footer> landmark closing every page (built 7.A.10, replacing the
   speculative 7.A.2 placeholder that no page ever used). Site DEFAULT
   background (--color-bg) with a hairline top separator (--color-border)
   dividing it from the section above — deliberately NOT a lifted surface and
   NOT the privacy band's recessed --color-surface. Shared across every page in
   7.A; consumes tokens only, never a hardcoded value. */
.footer{
  /* Extra breathing room above the hairline so the footer sits well clear of
     the section above it (the final-CTA's own bottom rhythm was reading too
     tight). Uses the fluid --space-section-y — the same full section-height
     rhythm the page's sections lead in with — so the gap scales with the
     viewport. Existing token, no raw value. */
  margin-block-start:var(--space-section-y);
  padding-block:var(--space-8);
  padding-inline:var(--container-pad-x);
  background:var(--color-bg);
  border-top:1px solid var(--color-border);
  color:var(--color-text-secondary);
  font-size:var(--text-sm);
}

.footer a{
  color:var(--color-text-secondary);
  /* Colour-only hover, like the nav + final-CTA links — no transform, so it is
     safe under reduced motion (matches the existing site pattern). */
  transition:color var(--dur-fast) var(--ease-standard);
}

.footer a:hover{
  color:var(--color-text);
}

/* Inner column: caps + centres the content like .container does (the .footer
   already supplies the page gutter), then stacks the two-column top row above
   the full-width copyright line. */
.footer-inner{
  max-width:var(--container-max);
  margin-inline:auto;
  display:flex;
  flex-direction:column;
  gap:var(--space-7);
}

/* Two columns on desktop (brand left, links right); a single stacked column on
   mobile. Comes apart at the shared 60rem breakpoint every other multi-column
   block on the site uses. */
.footer-top{
  display:flex;
  flex-direction:column;
  gap:var(--space-6);
}

@media (min-width: 60rem){
  .footer-top{
    flex-direction:row;
    align-items:flex-start;
    justify-content:space-between;
  }
}

/* Left column — the logo lockup above the tagline. */
.footer-brand{
  display:flex;
  flex-direction:column;
  gap:var(--space-3);
}

.footer-brand-link{
  display:inline-flex;
  align-items:center;
}

/* Footer logo — one space step smaller than the nav lockup (--space-6),
   swapped by [data-theme] exactly like the nav's .brand-logo: the light asset
   on the light site, the dark asset on the dark site. */
.footer-logo{
  height:var(--space-5);
  width:auto;
}

.footer-logo--dark{
  display:none;
}

[data-theme="dark"] .footer-logo--light{
  display:none;
}

[data-theme="dark"] .footer-logo--dark{
  display:block;
}

.footer-tagline{
  color:var(--color-text-tertiary);
}

/* Right column — vertical stack of links (Privacy / Terms / email / socials). */
.footer-links{
  display:flex;
  flex-direction:column;
  gap:var(--space-3);
}

/* ----- Footer trader-identity block (7.F) -----
   The seller-of-record disclosure required on every public surface
   (Decisions.md §1 "Trader-identity block", LOCKED): legal name, geographic
   business address, contact email, VAT number. Sits full-width between the
   two-column top row and the copyright line, separated by the same hairline
   the footer itself uses so it reads as a distinct legal notice rather than a
   fourth link column.

   DELIBERATELY QUIET, NOT HIDDEN. It takes --color-text-tertiary and the
   footer's inherited --text-sm — the same weight as the copyright — because a
   trader block is a disclosure, not a marketing element. What it must never
   become is small print: no font-size reduction below --text-sm, no opacity
   fade, no collapse-behind-a-toggle. The obligation is that a consumer can
   read it, and the LOCKED decision is explicit that the address is not to be
   omitted.

   The whole block is static markup on all five pages — it does NOT depend on
   JS. See trader.js for why the two OPTIONAL rows are the exception. */
.footer-trader{
  display:flex;
  flex-direction:column;
  gap:var(--space-2);
  padding-block-start:var(--space-6);
  border-top:1px solid var(--color-border);
  color:var(--color-text-tertiary);
}

/* The "Pre-launch legal information — VAT registration pending" label. Slightly
   stronger than the body beneath it so the pre-launch caveat is what the eye
   lands on first: this block is knowingly incomplete until the P.IVA is live,
   and the label is what makes that honest rather than an omission. */
.footer-trader-label{
  color:var(--color-text-secondary);
}

/* <address> carries the trader identity. base.css does not reset its UA italic,
   so it is normalised here — the block is a legal notice, not an aside. */
.footer-trader-body{
  font-style:normal;
  line-height:var(--leading-relaxed);
}

/* The two deferred rows (registration number, ODR link). Ship `hidden` and are
   revealed by trader.js only when a value is set — see that file. The explicit
   guard matters: `hidden` is a UA display:none, so ANY later `display` rule on
   this class would silently un-hide a row carrying no value. */
.footer-trader-slot[hidden]{
  display:none !important;
}

/* Full-width copyright, below both columns — small (inherits --text-sm), muted. */
.footer-copyright{
  color:var(--color-text-tertiary);
}

/* ----- Buttons ----- */
.btn{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:var(--space-2);
  /* Vertical padding slimmed --space-3 -> --space-2 (7.A.2 polish change
     3): the buttons read as too chunky. Horizontal padding (--space-5)
     and the pill radius are unchanged; at --text-base the resulting
     height stays a comfortable tap target. Shared here so the nav CTA and
     hero CTA match. */
  padding-block:var(--space-2);
  padding-inline:var(--space-5);
  border-radius:var(--radius-pill);
  border:1px solid transparent;
  font-size:var(--text-base);
  font-weight:var(--weight-medium);
  cursor:pointer;
  /* No font-size/padding morph (7.A.12): the nav CTA's compact size is now an
     instant state change, so only the interactive properties (fill, colour,
     border, press-scale) transition here. */
  transition:background var(--dur-fast) var(--ease-standard),
             color var(--dur-fast) var(--ease-standard),
             border-color var(--dur-fast) var(--ease-standard),
             transform var(--dur-fast) var(--ease-standard);
}

.btn:active{
  transform:scale(.97);
}

.btn-primary{
  background:var(--color-text);
  color:var(--color-text-inverse);
}

.btn-primary:hover{
  background:var(--color-text-secondary);
}

.btn-secondary{
  background:transparent;
  border-color:var(--color-border-strong);
  color:var(--color-text);
}

.btn-secondary:hover{
  background:var(--color-fill-selected);
}

/* Disabled state — shared across button variants. Reads as clearly inert:
   dimmed, not-allowed cursor, and no press-scale. First used by the final-CTA
   waitlist submit (7.A.9), which ships `disabled` until 7.C wires the backend
   and removes the attribute. A truly-disabled <button> receives no hover or
   pointer events, so the per-variant :hover fills above never apply here — no
   hover reset is needed. Opacity is a visual-effect value, NOT one of the six
   token-governed categories (colour, spacing, radius, type, shadow, motion),
   so a raw 0.5 gets the same latitude as the nav's blur radius (7.A.1). */
.btn:disabled{
  opacity:.5;
  cursor:not-allowed;
}

.btn:disabled:active{
  transform:none;
}

/* ----- Card ----- */
.card{
  background:var(--color-surface-raised);
  border:1px solid var(--color-border);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-md);
  padding:var(--space-6);
}

/* ----- CTA stub pattern -----
   Every waitlist CTA on this site, in every sub-phase, is this exact
   shape. A real relative href to the path 7.C will occupy — never
   href="#" (scrolls to top, looks broken in review) and never
   mailto: or a disabled button (both get ripped out later; a real
   path 404s honestly until 7.C ships, and needs zero markup edits
   once it does). data-cta="waitlist" is the hook 7.H's self-hosted
   counter reads.

   <a href="/waitlist" data-cta="waitlist" class="btn btn-primary">Join the waitlist</a>

   Never write "Start free trial" anywhere on this site — Stripe is
   not wired and no signed installer exists yet. Every CTA points at
   the waitlist. */

/* ----- Modal -----
   MOVED HERE FROM pricing.css IN 7.C, declarations unchanged. It was authored in
   7.A.12 for the one dialog the site had (/pricing/'s "Try RawCloser"); 7.C adds
   the form success modal to the landing final-CTA, /waitlist/ and /tester/, so
   it is now on four of five pages and belongs in the shared sheet. pricing.css
   keeps a pointer where the block used to be.

   A real dialog: role="dialog" + aria-modal="true" + aria-labelledby its heading
   (markup). The behaviour is JS's — focus moves in and is trapped,
   Escape / backdrop / the close control all close it, focus returns to the
   opener, and background scroll is locked (html.modal-open below) in a way that
   cooperates with the damped-scroll wrapper rather than fighting it. TWO
   implementations of that behaviour exist — pricing.js for the pricing dialog,
   assets/js/modal.js for 7.C's — and modal.js records why they were not merged.

   The backdrop is a sibling of the .smooth wrapper (markup), so the damped-scroll
   transform never touches it. It sits above the nav via a z-index derived from
   the existing --z-nav token (no raw stacking value invented). It starts with
   the `hidden` attribute (fully inert, out of the a11y tree); the script removes
   `hidden` then adds .is-open on the next frame to run the entrance. */
.modal-backdrop{
  position:fixed;
  inset:0;
  z-index:calc(var(--z-nav) + 10);   /* above the fixed nav (--z-nav) */
  display:flex;
  align-items:center;
  justify-content:center;
  padding:var(--space-5);
  /* Scrim: a translucent black wash over the page. rgba() alpha is a
     visual-effect value (not a token-governed category), the same latitude the
     nav/glass blur radii take. */
  background:rgba(0,0,0,.5);
  opacity:0;
  transition:opacity var(--dur-slow) var(--ease-standard);
}

/* Re-assert display:none for the closed (hidden-attribute) state — the
   display:flex above would otherwise override the [hidden] default. */
.modal-backdrop[hidden]{
  display:none;
}

.modal-backdrop.is-open{
  opacity:1;
}

/* The dialog panel — the DEFAULT raised surface, lifted well above the page with
   the largest shadow so it reads as a floating modal. Capped narrow and centred;
   scrolls internally if it ever exceeds the viewport (dvh with a vh fallback).
   30rem is a structural rem cap, not a token. */
.modal-dialog{
  position:relative;
  width:100%;
  max-width:30rem;
  max-height:90vh;
  max-height:90dvh;
  overflow-y:auto;
  background:var(--color-surface-raised);
  border:1px solid var(--color-border);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-xl);
  padding:var(--space-7);
  /* Entrance: a small rise + settle, matched to the backdrop fade. Cleared to
     the resting position by .is-open below. */
  transform:translateY(var(--space-3)) scale(.98);
  transition:transform var(--dur-slow) var(--ease-gentle);
}

.modal-backdrop.is-open .modal-dialog{
  transform:none;
}

/* The dialog itself takes focus on open (tabindex="-1" in markup) so the label
   is announced; it must not paint a focus ring for that programmatic focus. */
.modal-dialog:focus{
  outline:none;
}

/* Visible close control — top-right icon button, transparent, quiet tone,
   brightening on hover/focus. Keyboard focus uses the global :focus-visible ring
   (base.css). 2rem box is a structural tap target, not a token. */
.modal-close{
  position:absolute;
  top:var(--space-4);
  right:var(--space-4);
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:2rem;
  height:2rem;
  padding:0;
  border:0;
  border-radius:var(--radius-pill);
  background:transparent;
  color:var(--color-text-secondary);
  cursor:pointer;
  transition:color var(--dur-fast) var(--ease-standard),
             background var(--dur-base) var(--ease-standard);
}

.modal-close:hover,
.modal-close:focus-visible{
  color:var(--color-text);
  background:var(--color-fill-selected);
}

.modal-close svg{
  width:1rem;                   /* structural icon dim */
  height:1rem;
  display:block;
}

/* Heading — the dialog's accessible name (aria-labelledby points here). Sized so
   it clears the close control on its right. */
.modal-title{
  font-size:var(--text-xl);
  padding-right:var(--space-6);
}

/* Body paragraph — secondary tone, relaxed leading, capped to a comfortable
   measure inside the narrow dialog. */
.modal-body{
  font-size:var(--text-base);
  color:var(--color-text-secondary);
  line-height:var(--leading-relaxed);
  margin-top:var(--space-4);
}

/* Stacked actions, full width, so several read as equal-weight ways on. Reuse
   .btn / .btn-primary / .btn-secondary above. The pricing dialog has two; the
   7.C success modals have one or two depending on the form. */
.modal-actions{
  display:flex;
  flex-direction:column;
  gap:var(--space-3);
  margin-top:var(--space-6);
}

.modal-actions .btn{
  width:100%;
}

/* ----- Background scroll lock (cooperating with the damped scroll) -----
   The script adds .modal-open to <html> while the dialog is open. Locking the
   scroll ROOT with overflow:hidden freezes window.scrollY, so smooth.js — which
   only ever reacts to scroll/resize events and translates .smooth by the lag
   toward window.scrollY — receives no events and holds .smooth exactly where it
   is. No competing transform, no jump on open or close: the two cooperate rather
   than fight. (On the Mac-first audience, overlay scrollbars mean locking adds no
   scrollbar-gutter shift either.) */
html.modal-open{
  overflow:hidden;
}

/* ----- Reduced motion ----- */
/* The modal appears with NO animation: no fade, no rise. It is fully functional
   (opens, traps focus, closes) — only the tweens are dropped. --dur-slow is
   already zeroed in base.css under reduced motion, so the transitions collapse;
   this belt-and-suspenders guard also clears the dialog's start transform so
   there is zero chance of a residual offset. */
@media (prefers-reduced-motion: reduce){
  .modal-backdrop,
  .modal-dialog{
    transition:none;
  }
  .modal-dialog{
    transform:none;
  }
}

/* ----- Nav dropdown panel, Phase 7.A.32 -----
   The .nav-menu-btn button + mobile item-swap live earlier, by the nav rules
   they size (§Mobile nav dropdown). This half is the panel itself, REPLACING
   7.A.31's .nav-drawer-backdrop/.nav-drawer (a full-height right-side panel
   that reused .modal-backdrop/.modal-dialog + assets/js/modal.js).

   NOT built on .modal-backdrop/.modal-dialog this time, and deliberately so.
   modal.js's contract is a CENTRED, full-viewport dialog: a scrim that covers
   the whole page (its click IS the outside-tap detector), and a background-
   scroll lock it applies on every open (html.modal-open). Neither fits a
   panel that is "sized to its contents… not full height, not full width, not
   an overlay covering the page" — there is no scrim here for modal.js to find
   or lock scroll behind, so forcing this into .modal-dialog would mean either
   adding a full-page backdrop this design explicitly doesn't want, or hacking
   modal.js's open()/close() around a backdrop that isn't there. Cheaper and
   clearer to give it its own small controller instead: assets/js/nav-menu.js,
   which owns exactly `hidden` + one open class, an outside-click listener, and
   a focusout close — see that file for the full reasoning.

   ANCHORING: .nav-menu (just above, in the nav rules) is the position:relative
   box this panel positions itself against — the BUTTON's own footprint, not
   the wider nav bar — so top-right stays anchored to the button regardless of
   how much room the links/CTA take on a given page. position:absolute takes it
   out of flow entirely, so opening it can never shift any layout beneath it.
   SIZED TO CONTENT: no width/height percentage anywhere, no min-width — the
   panel is exactly as big as its three items, the opposite of the drawer's
   85%-of-viewport-height panel it replaces.
   PX TOKENS ONLY (7.A.31's own file-header note applies here too): every
   dimension below is a --space-* /--radius-* token, all literal px in
   tokens.css, so the panel's size and position never track the visitor's
   root font-size / text-scaling setting — only its TEXT (--text-base) scales,
   as it should. */
.nav-dropdown{
  position:absolute;
  top:calc(100% + var(--space-2));
  right:0;
  z-index:calc(var(--z-nav) + 10);   /* same layer as the success modal */
  display:flex;
  flex-direction:column;
  gap:var(--space-1);
  padding:var(--space-2);
  background:var(--color-surface-raised);
  border:1px solid var(--color-border);
  border-radius:var(--radius-md);
  box-shadow:var(--shadow-lg);
  transform-origin:top right;
  transform:translateY(calc(-1 * var(--space-2))) scale(.96);
  opacity:0;
  transition:opacity var(--dur-base) var(--ease-standard),
             transform var(--dur-base) var(--ease-standard);
}

/* Re-assert display:none for the closed (hidden-attribute) state — the
   display:flex above would otherwise override the [hidden] default, same
   reasoning as .modal-backdrop[hidden] above. */
.nav-dropdown[hidden]{
  display:none;
}

.nav-dropdown.is-open{
  opacity:1;
  transform:none;
}

.nav-dropdown a:not(.btn){
  display:block;
  font-size:var(--text-base);
  font-weight:var(--weight-medium);
  color:var(--color-text);
  padding-block:var(--space-2);
  padding-inline:var(--space-3);
  border-radius:var(--radius-sm);
  transition:background var(--dur-fast) var(--ease-standard);
}

.nav-dropdown a:not(.btn):hover,
.nav-dropdown a:not(.btn):focus-visible{
  background:var(--color-fill-selected);
}

.nav-dropdown .btn{
  width:100%;
  margin-top:var(--space-1);
}

@media (prefers-reduced-motion: reduce){
  .nav-dropdown{
    transition:none;
    transform:none;
  }
}

/* ----- Form privacy notice (7.C) -----
   The point-of-collection notice under each of the three sign-up forms, linking
   to /forms-privacy/. STATIC MARKUP on all three surfaces, never rendered by
   JS, for the reason the trader block records: a disclosure that needs
   JavaScript to appear is not a disclosure.

   The quietest text in its section — the site's fine-print treatment
   (--text-sm + --color-text-tertiary, the same pair .hero-req and .wl-fine
   use). It is fine print, and pretending otherwise by weighting it up would
   only compete with the offer line it sits beside. Its LINK carries normal body
   colour with an underline so it does not disappear into the muted line: this
   is the one piece of fine print a visitor must be able to see is clickable.

   The three consumers position it themselves — .r-rise on the landing
   final-CTA (it lives in a scroll-reveal section), .wl-notice / .ts-notice for
   the two on-load hero staggers. This rule carries type and colour only. */
.form-notice{
  margin-top:var(--space-4);
  font-size:var(--text-sm);
  color:var(--color-text-tertiary);
  line-height:var(--leading-relaxed);
}

.form-notice a{
  color:var(--color-text-secondary);
  text-decoration:underline;
  text-underline-offset:2px;
  transition:color var(--dur-fast) var(--ease-standard);
}

.form-notice a:hover{
  color:var(--color-text);
}

/* ----- Form status line (7.C) -----
   The one place a submission failure is rendered. Ships with the `hidden`
   attribute and role="alert" in the markup, so it is out of the a11y tree until
   there is something to say and is announced the moment there is.

   NEGATIVE TONE, and --color-negative is the token for it in both themes. Only
   failures land here: success opens the modal, so there is no success variant
   of this line and nothing to distinguish by colour. */
.form-status{
  margin-top:var(--space-4);
  font-size:var(--text-sm);
  color:var(--color-negative);
  line-height:var(--leading-relaxed);
}

.form-status[hidden]{
  display:none;
}

/* ----- No-JS form fallback (7.C) -----
   The line inside each form's <noscript>. Only a visitor with scripting
   disabled ever renders it, so this rule is dead weight for everyone else, and
   that is fine — it is four declarations.

   SECONDARY tone, not the failure line's negative: nothing has gone wrong, the
   visitor is simply being told the route that does work. Otherwise the same
   fine-print treatment as .form-notice, because it sits in the same stack and
   an argument about which quiet line is quieter would be a distinction with no
   reader. Its link takes an underlined treatment for the same reason that one
   does: a mailto nobody can see is not a way through. */
.form-noscript{
  margin-top:var(--space-4);
  font-size:var(--text-sm);
  color:var(--color-text-secondary);
  line-height:var(--leading-relaxed);
}

.form-noscript a{
  color:var(--color-text);
  text-decoration:underline;
  text-underline-offset:2px;
  transition:color var(--dur-fast) var(--ease-standard);
}

.form-noscript a:hover{
  color:var(--color-text-secondary);
}

/* ----- Honeypot field (7.C) -----
   The `website` input every form now carries. Hidden from sighted users AND
   from screen readers (aria-hidden on the wrapper, in the markup) AND from the
   tab order (tabindex="-1" on the input, in the markup).

   NOT display:none, deliberately. A bot that skips display:none fields skips
   the trap entirely, and a wrapper this far off-screen is invisible by any
   measure that matters to a person. The 1px box + overflow:hidden is the same
   clip pattern .visually-hidden uses (base.css); the difference is that this
   one is also removed from the accessibility tree, which .visually-hidden must
   never be.

   left:-9999px rather than clip-path: a field with no layout box at all is
   another signal a bot can read. This one is laid out, painted, and simply
   somewhere nobody will ever look. */
.hp-field{
  position:absolute;
  left:-9999px;
  top:auto;
  width:1px;
  height:1px;
  overflow:hidden;
}
