/* Shared UI components used by BOTH the umbrella homepage and the product sub-sites.
   ⚠️ THIS FILE EXISTS BECAUSE index.html DOES NOT LOAD product.css. It is self-contained with its
   own inline <style>, so anything appended to product.css silently does nothing there. The animated
   logo shipped that way and rendered as 16px Inter body text on the homepage for a full deploy —
   it looked deliberate in a screenshot, which is exactly why it survived review.
   Put anything both surfaces need HERE, never in product.css.

   Accent tokens carry fallbacks: product pages set --acc/--accd/--tint per product, the umbrella
   sets none, so every use has to survive their absence. */

/* ============================================================================
   SELF-HOSTED WEBFONTS -- GENERATED by scripts/gen-fonts.mjs. Do not hand-edit
   between the markers below: filenames are content-hashed because assets/**
   is served with `max-age=31536000, immutable`, so a rebuilt font only reaches
   anyone if its URL changes too (shared-ui.css itself is max-age=300, so a
   fresh reference here propagates in minutes). Re-run the generator, never
   hand-edit its output.
   ========================================================================== */
/* gen-fonts:start */
@font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;src:url('assets/fonts/inter-b5faee65e0.woff2') format('woff2')}
@font-face{font-family:'Outfit';font-style:normal;font-weight:100 900;font-display:swap;src:url('assets/fonts/outfit-d303e22642.woff2') format('woff2')}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:400 500;font-display:swap;src:url('assets/fonts/jetbrains-mono-daae1202a7.woff2') format('woff2')}
/* gen-fonts:end */

/* ============================================================================
   ANIMATED LOGO SYSTEM
   Ported verbatim from the Claude Design project "# MZE IQ Unified Brand System"
   (MZE IQ Logos v1). Every primitive is scoped under .mzlogo so the kit's own
   class names (.aDraw, .aPop, …) can be pasted in unchanged without ever leaking
   into page styles. Markup is injected by logos.js; colour comes from --acc /
   --accd via currentColor, so each page's existing accent drives the logo.
   ========================================================================== */
.mzlogo{display:inline-flex;align-items:center;gap:clamp(18px,2.4vw,26px);color:var(--acc,#19D9C6)}
.light .mzlogo{color:var(--accd,#00857B)}
/* The five satellite nodes of the HOUSE mark only — each carries its product's own accent, which is
   what makes the umbrella emblem read as "one mark, five signals" rather than a plain teal wheel.
   Values are brand.json's product day/night pairs verbatim; do not derive them. The night set is
   the default because the only surface this mark appears on today (index.html's hero) is dark, and
   index.html sets no data-theme at all. Product emblems are monochrome and never touch these. */
/* ---- glow ----
   A GLOW is light a thing EMITS: zero offset, zero spread, one radius. A drop shadow has offset
   and is about elevation -- a different system, deliberately not tokenised with these, which is
   why this repo has ten box-shadows that are none of the site's business here.
   The radius is the shared part; the COLOUR is not, and should not be: the house rail glows teal
   and a product rail glows that product's accent. --glow-a is the emission strength for the
   tinted case. Two sizes is the whole scale, because a survey found exactly two. */
:root{--glow-sm:0 0 8px;--glow-md:0 0 14px;--glow-a:.6}

.mzlogo{--n-mliq:#40AEFC;--n-paperiq:#3FC98A;--n-prodiq:#E9A64A;--n-meetupiq:#7269F2;--n-piniq:#E9607A}
[data-theme=light] .mzlogo{--n-mliq:#0B7ACA;--n-paperiq:#258D59;--n-prodiq:#926B19;
  --n-meetupiq:#4F46E5;--n-piniq:#C9314A}
.mzlogo svg{flex:none;overflow:visible}
.mzlogo .lgWord{font-family:var(--dsp);font-weight:700;letter-spacing:-.02em;line-height:1;
  font-size:clamp(30px,4.4vw,54px);color:var(--head)}
/* Opt IN to light text, never out. The product heroes carry no .light class and sit on a white
   body, so a dark-surface default rendered the wordmark near-white on white — invisible on every
   product page until a phone screenshot caught it. Only index.html's hero is truly dark. */
.mzlogo[data-on="dark"] .lgWord{color:#EAF2F1}
.mzlogo[data-on="dark"] .lgTag{color:rgba(232,242,240,.66)}
.mzlogo .lgWord i{font-style:normal;color:currentColor}
.mzlogo .lgTag{font-family:var(--mono);font-size:clamp(10px,1.1vw,12.5px);letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted);margin-top:10px}

/* ---- hover ---- */
/* An ACCENT, not a replay. 160ms, transform only, and it never touches the ambient spins or any
   entrance that starts invisible -- see the long note in logos.js for what this replaced and why.
   Scaling the root <svg> composes with the inner per-element transforms rather than fighting them,
   and .mzlogo svg already carries overflow:visible so 5% cannot clip. */
.lgMark{display:block;flex:none;line-height:0;transition:transform .16s cubic-bezier(.22,1,.36,1)}
.mzlogo:hover .lgMark{transform:scale(1.05)}

/* ---- animation primitives ---- */
.mzlogo .aDraw{stroke-dasharray:340;stroke-dashoffset:340;animation:lgDraw 2.8s cubic-bezier(.22,1,.36,1) forwards}
/* Short paths (MeetupIQ's five spokes) need a 16-unit dash, not the 340 the long outlines use.
   ⚠️ This MUST stay a class. It was an inline style until 2026-08-24, and an inline declaration
   outranks every selector short of !important — so the reduced-motion reset below could not reach
   it and the spokes rendered fully dashed out, i.e. INVISIBLE, for anyone with reduced motion on.
   Confirmed by screenshotting Chrome with --force-prefers-reduced-motion, not by reading. */
.mzlogo .aShort{stroke-dasharray:16;stroke-dashoffset:16}
.mzlogo .aPop{transform-box:fill-box;transform-origin:center;opacity:0;animation:lgPop 1.5s cubic-bezier(.22,1,.36,1) forwards}
.mzlogo .aLetter{display:inline-block;white-space:pre;opacity:0;animation:lgLetter 1.5s cubic-bezier(.22,1,.36,1) forwards}
.mzlogo .aPulse{transform-box:fill-box;transform-origin:center;animation:lgPulse 5s ease-out infinite}
.mzlogo .aWipe{transform-box:fill-box;transform-origin:left center;transform:scaleX(0);animation:lgWipe 1.4s cubic-bezier(.22,1,.36,1) forwards}
.mzlogo .aRise{transform-box:fill-box;transform-origin:bottom center;transform:scaleY(0);animation:lgRise 1.4s cubic-bezier(.22,1,.36,1) forwards}
.mzlogo .aScan{animation:lgScan 6s ease-in-out infinite}
.mzlogo .aFadeUp{opacity:0;animation:lgFadeUp 1.5s cubic-bezier(.22,1,.36,1) forwards}
.mzlogo .aConv{transform-box:fill-box;transform-origin:center;opacity:0;animation:lgConv 1.7s cubic-bezier(.19,1,.28,1) forwards}
.mzlogo .aRing{transform-box:fill-box;transform-origin:center;opacity:0;animation:lgRingIn 1s ease .35s forwards, lgRingSpin 34s linear infinite}
.mzlogo .aRingR{transform-box:fill-box;transform-origin:center;opacity:0;animation:lgRingIn 1s ease .5s forwards, lgRingSpinR 22s linear infinite}
.mzlogo .aSnap{transform-box:fill-box;transform-origin:center;opacity:0;animation:lgSnap 1.1s cubic-bezier(.16,1.5,.4,1) 1.05s forwards}
.mzlogo .borj{transform-box:fill-box;transform-origin:center;animation:borjIn 1.6s ease forwards, borjSpin 20s linear infinite}
.mzlogo .dly1{animation-delay:.10s}.mzlogo .dly2{animation-delay:.22s}.mzlogo .dly3{animation-delay:.34s}
.mzlogo .dly4{animation-delay:.46s}.mzlogo .dly5{animation-delay:.58s}.mzlogo .dly6{animation-delay:.70s}
.mzlogo .dly7{animation-delay:.82s}.mzlogo .dly8{animation-delay:.94s}

@keyframes lgDraw{to{stroke-dashoffset:0}}
@keyframes lgPop{0%{opacity:0;transform:scale(.55)}100%{opacity:1;transform:scale(1)}}
@keyframes lgLetter{0%{opacity:0;transform:translateY(.42em)}100%{opacity:1;transform:translateY(0)}}
@keyframes lgPulse{0%{transform:scale(1);opacity:.5}70%{opacity:0}100%{transform:scale(2.5);opacity:0}}
@keyframes lgWipe{to{transform:scaleX(1)}}
@keyframes lgRise{to{transform:scaleY(1)}}
@keyframes lgScan{0%{transform:translateY(0);opacity:0}12%{opacity:.95}86%{opacity:.95}100%{transform:translateY(30px);opacity:0}}
@keyframes lgFadeUp{from{opacity:0;transform:translateY(11px)}to{opacity:1;transform:translateY(0)}}
@keyframes lgConv{0%{opacity:0;transform:translate(var(--dx,0px),var(--dy,0px)) scale(.45)}
  30%{opacity:1}72%{transform:translate(0,0) scale(1.22)}100%{opacity:1;transform:translate(0,0) scale(1)}}
@keyframes lgRingIn{to{opacity:.5}}
@keyframes lgRingSpin{to{transform:rotate(360deg)}}
@keyframes lgRingSpinR{to{transform:rotate(-360deg)}}
@keyframes lgSnap{0%{opacity:0;transform:scale(.2)}100%{opacity:1;transform:scale(1)}}
@keyframes borjSpin{to{transform:rotate(360deg)}}
@keyframes borjIn{from{opacity:0}to{opacity:1}}

/* The kit shipped this block and it is not optional — the borjgali spins forever
   and the signal nodes pulse forever, which is exactly what vestibular-sensitive
   users ask the OS to stop. */
@media (prefers-reduced-motion: reduce){
  .mzlogo .aDraw,.mzlogo .aShort{stroke-dashoffset:0;animation:none}
  .mzlogo .aPop,.mzlogo .aWipe,.mzlogo .aRise{opacity:1;transform:none;animation:none}
  .mzlogo .aFadeUp{opacity:1;animation:none}
  .mzlogo .aLetter{opacity:1;transform:none;animation:none}
  .mzlogo .aScan,.mzlogo .aPulse{animation:none}
  .mzlogo .aConv,.mzlogo .aSnap{opacity:1;transform:none;animation:none}
  .mzlogo .aRing,.mzlogo .aRingR{opacity:.5;animation:none}
  .mzlogo .borj{animation:none;opacity:1}
  /* The hover accent is motion too, and a 5% jump is exactly the kind vestibular-sensitive users
     asked the OS to stop. The lockup stays interactive, it just does not move. */
  .lgMark{transition:none}
  .mzlogo:hover .lgMark{transform:none}
}


/* ---------- nav product menu ---------- */
/* The three-line button and its panel. Rendered on a light ground on every page, so its colours are
   fixed rather than inherited — the nav itself is translucent over both dark heroes and light
   sections, and a panel that changed contrast with the section behind it would be unreadable half
   the time. */
/* ---- nav control hairline (founder pick A, 2026-09-14) ----
   A one-pixel dark ring sits OUTSIDE the existing border. The nav controls sit on a <canvas>, not
   on a colour: every CSS background in the stack is transparent and the hero canvas paints
   NEAR-WHITE in both themes, so the dark-theme white border landed on rgb(243,243,241) at 1.04:1.
   A light border plus a dark hairline means whichever band it lands on, one of the two reads --
   the same principle as the focus ring below, at 1px instead of 4.
   Zero blur, so the G12 classifier treats it as a ring and not a glow. */
#nav .links a.pill,
#nav .tgl,
.navmenu-btn,
#snd{box-shadow:0 0 0 1px rgba(5,13,12,.55)}

/* ---- nav focus ring ----
   Nav controls had NO :focus-visible rule at all, so they took Chromium default near-black ring and
   measured 1.02-1.75:1 on the dark hero. At 390px the hamburger is the ONLY route to the section
   links, so this is the one focus indicator that must never be invisible.
   TWO TONES, not one, because the nav band changes per PAGE inside a single theme: it sits over the
   WebGL hero on index.html and over white on a product page in light theme. Measured, no single
   colour clears 3:1 on both -- teal is 1.78 on white, ink is 1.60 on the hero, white is 1.00 on
   white. A white inner ring with an ink outer ring means whichever band it lands on, one of the two
   contrasts; worst case across all three bands is 11.51:1.
   The outer ring is box-shadow with ZERO blur, so the G12 classifier reads it as a focus ring and
   not a glow -- that case is in scripts/check-glow-tripwire.mjs. */
#nav .links a.pill:focus-visible,
#nav .tgl:focus-visible,
.navmenu-btn:focus-visible,
#snd:focus-visible{outline:2px solid #fff;outline-offset:2px;box-shadow:0 0 0 4px rgba(5,13,12,.92)}

.navmenu-btn{display:grid;gap:4px;padding:9px 10px;border:1px solid rgba(255,255,255,.38);
  border-radius:999px;background:rgba(255,255,255,.05);cursor:pointer;flex:none}
.navmenu-btn i{display:block;width:17px;height:1.6px;border-radius:2px;background:rgba(232,242,240,.86);
  transition:transform .26s var(--ease),opacity .2s var(--ease)}
.navmenu-btn:hover{border-color:rgba(255,255,255,.45)}
.navmenu-btn.on i:nth-child(1){transform:translateY(5.6px) rotate(45deg)}
.navmenu-btn.on i:nth-child(2){opacity:0}
.navmenu-btn.on i:nth-child(3){transform:translateY(-5.6px) rotate(-45deg)}
/* ⚠️ `.light` is the light-BAND class, and the nav is never inside a band — so on the five product
   pages (light theme by default) this override never applied, and the bars painted
   rgba(232,242,240,.86) on #FFFFFF: 1.12:1, effectively invisible. At 390px this button is the only
   route to the section links. Key it on the theme as well. Same bug family as the legal-page nav. */
.light .navmenu-btn,[data-theme=light] .navmenu-btn{border-color:rgba(36,44,57,.55);background:rgba(36,44,57,.04)}
.light .navmenu-btn i,[data-theme=light] .navmenu-btn i{background:rgba(36,44,57,.8)}
[data-theme=light] .navmenu-btn:hover{border-color:rgba(36,44,57,.62)}
@media(prefers-reduced-motion:reduce){.navmenu-btn i{transition:none}}

.navmenu{position:absolute;top:calc(100% + 10px);right:var(--pad);width:min(340px,calc(100vw - 28px));
  background:#fff;border:1px solid var(--line);border-radius:var(--rl);
  box-shadow:0 26px 70px -24px rgba(5,13,12,.42);overflow:hidden;z-index:8100}
.navmenu[hidden]{display:none}
.nm-inner{padding:10px}
.nm-h{font-size:9.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
  padding:12px 12px 7px}
.nm-item{display:flex;align-items:center;gap:11px;padding:10px 12px;border-radius:var(--rs);
  color:var(--head);transition:background .16s}
.nm-item:hover{background:var(--tint,#E4F3F1);color:var(--head)}
.nm-item s{flex:none;width:9px;height:9px;border-radius:999px;text-decoration:none}
.nm-item span{display:grid;min-width:0}
.nm-item b{font-family:var(--dsp);font-weight:700;font-size:15px;letter-spacing:-.01em;line-height:1.2}
.nm-item em{font-style:normal;font-family:var(--mono);font-size:9.5px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--muted);margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nm-item u{margin-left:auto;text-decoration:none;font-size:8.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);flex:none}
.nm-current{background:var(--tint,#E4F3F1)}
.nm-secs{display:flex;flex-wrap:wrap;gap:6px;padding:0 12px 4px}
.nm-sec{font-size:12.5px;color:var(--body);border:1px solid var(--line);border-radius:999px;
  padding:5px 11px}
.nm-sec:hover{background:var(--tint,#E4F3F1);color:var(--head)}
.nm-all,.nm-login{display:block;margin:8px 12px 4px;padding:11px 14px;border-radius:var(--rs);
  font-size:13.5px;font-weight:500;text-align:center}
.nm-all{border:1px solid var(--line);color:var(--head)}
.nm-all:hover{background:var(--tint,#E4F3F1);color:var(--head)}
.nm-login{background:var(--acc,#00857B);color:#fff;margin-bottom:8px}
.nm-login:hover{background:var(--accd,#00615A);color:#fff}
/* [data-theme=light] a in product.css is (0,0,1,1) and outranks the (0,0,1,0) .nm-login rule above,
   so the white was discarded and the label painted in the deepened accent ON the accent pill —
   1.26:1 to 1.51:1 on all five product pages. Re-assert it at (0,0,2,0). */
[data-theme=light] .nm-login,[data-theme=light] .nm-login:hover{color:#fff}
@media(max-width:640px){
  .navmenu{right:14px;left:14px;width:auto}
}


/* ---------- sample-data tag ---------- */
/* Every interactive demo on this site runs on fixtures, and until 2026-08-24 nothing said so —
   the section eyebrows read "Live · capacity" directly above hard-coded data, and one caption
   claimed "real crew commitments". A visitor reasonably reads that as a screenshot of a customer's
   account. Put this on any panel whose numbers are illustrative. It is deliberately quiet: the
   point is disclosure, not a disclaimer that swamps the demo. */
.sample-tag{display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:9.5px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--muted);border:1px solid var(--line);
  border-radius:999px;padding:4px 9px;vertical-align:middle}
.sample-tag::before{content:"";width:5px;height:5px;border-radius:999px;background:currentColor;
  opacity:.6;flex:none}
[data-theme=light] .sample-tag{border-color:var(--line);color:var(--muted)}

/* ---------- site footer ---------- */
/* Lives here, not in product.css, because index.html does not load product.css — the same trap that
   shipped the animated logo as 16px body text for a full deploy. Both surfaces already define
   `footer{background:var(--ink)}` and a flex `footer .wrap`; .sitefoot keeps the former and has to
   out-specify the latter, hence `footer.sitefoot .wrap` rather than a bare class. */
footer.sitefoot .wrap{display:block}
.sf-grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:38px;padding:12px 0 34px}
.sf-brand .sf-name{font-family:var(--dsp);font-weight:700;font-size:16px;letter-spacing:-.01em;
  color:#EAF2F1}
.sf-brand p{margin:14px 0 0;max-width:34ch;line-height:1.6;color:rgba(232,242,240,.5)}
.sf-brand .sf-org{margin-top:16px;font-family:var(--mono);font-size:11px;letter-spacing:.06em;
  color:rgba(232,242,240,.36)}
.sf-col h2{font-size:10px;letter-spacing:.15em;text-transform:uppercase;font-weight:600;
  color:rgba(232,242,240,.42);margin:0 0 15px}
.sf-col a{display:block;margin-bottom:11px;color:rgba(232,242,240,.62);
  transition:color .16s var(--ease,ease)}
.sf-col a:hover{color:#EAF2F1}
/* The current product is listed but not a link to itself — a self-link in a cross-sell column
   reads as a dead control. */
.sf-col span[aria-current]{display:block;margin-bottom:11px;color:rgba(232,242,240,.34)}
.sf-base{border-top:1px solid rgba(255,255,255,.08);padding-top:20px;display:flex;flex-wrap:wrap;
  gap:12px;justify-content:space-between;align-items:center}
.sf-meta{font-family:var(--mono);opacity:.5}
/* ⚠️ LIGHT THEME IS THE DEFAULT on every product page, and [data-theme=light] redefines
   --ink to #FFFFFF — so the footer surface flips from near-black to white while the rules above
   still paint near-white text. Caught by rendering paperiq.html at 390px, where the whole footer
   came out white-on-white; it is the same failure as the near-white wordmark this file already
   documents. index.html never sets data-theme, so it keeps the dark values above.
   The base-rule-dark / [data-theme=light]-override pattern is what `footer` itself already does. */
[data-theme=light] .sf-brand .sf-name{color:var(--head)}
[data-theme=light] .sf-brand p{color:var(--body)}
[data-theme=light] .sf-brand .sf-org{color:var(--muted)}
[data-theme=light] .sf-col h2{color:var(--muted)}
[data-theme=light] .sf-col a{color:var(--body)}
[data-theme=light] .sf-col a:hover{color:var(--head)}
[data-theme=light] .sf-col span[aria-current]{color:rgba(36,44,57,.42)}
[data-theme=light] .sf-base{border-top-color:var(--line)}

@media(max-width:860px){
  .sf-grid{grid-template-columns:1fr 1fr;gap:30px 24px}
  .sf-brand{grid-column:1/-1}
}
@media(max-width:420px){
  .sf-grid{grid-template-columns:1fr}
  .sf-brand p{max-width:none}
}
@media(prefers-reduced-motion:reduce){.sf-col a{transition:none}}

/* ---------- lead capture ---------- */
/* One form, identical markup on all six pages, sitting directly above the footer. It lives here and
   not in product.css because index.html does not load product.css — the same trap this file already
   documents twice. Base rules are written for DARK and [data-theme=light] overrides them, because
   light is the default on every product page while index.html never sets the attribute.
   ⚠️ The submit button deliberately does NOT reuse .btn.p. index.html holds a private fork of that
   rule whose fill is the un-deepened --acc, measured at 4.17:1 against its white label; one
   definition here is correct on all six pages. --acc/--accd are product-page tokens that
   index.html defines neither of, hence the fallbacks to the house teal. */
.leadform{background:var(--ink);padding:clamp(52px,7vw,84px) 0 clamp(38px,5vw,60px)}
.lf{max-width:62ch}
.lf h2{font-family:var(--dsp);font-weight:700;letter-spacing:-.02em;line-height:1.08;
  font-size:clamp(24px,3vw,34px);color:#EAF2F1;margin:0}
.lf .lf-sub{margin:13px 0 0;line-height:1.6;color:rgba(232,242,240,.62)}
.lf-row{display:flex;flex-wrap:wrap;gap:12px;margin-top:24px}
.lf-field{flex:1 1 230px;min-width:0}
.lf-field.wide{flex-basis:100%}
.lf label{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;
  text-transform:uppercase;color:rgba(232,242,240,.52);margin-bottom:7px}
/* Border alpha is .38, not the .18 this shipped with, and the number is a measurement not a
   taste: WCAG 1.4.11 asks 3:1 of a CONTROL BOUNDARY, and .18 white composited over the --ink band
   measures 1.65:1 -- confirmed identically by calculation and in a real browser. .34 is the bare
   minimum (3.04); .38 gives 3.52 and a little margin. ⚠️ Measure the COMPOSITE, never the literal:
   the border carries alpha, so reading rgba(255,255,255,.18) as if it were opaque white is the
   measurement bug that hid this on every field since 2026-08-31. */
.lf input,.lf textarea,.lf select{width:100%;font-family:var(--sans);font-size:15px;line-height:1.5;
  padding:12px 14px;border-radius:var(--rs);border:1px solid rgba(255,255,255,.38);
  background:rgba(255,255,255,.04);color:#EAF2F1;-webkit-appearance:none;appearance:none}
.lf textarea{min-height:88px;resize:vertical}
.lf input::placeholder,.lf textarea::placeholder{color:rgba(232,242,240,.32)}
.lf input:focus-visible,.lf textarea:focus-visible,.lf select:focus-visible{outline:2px solid var(--teal-lit,#19D9C6);
  outline-offset:2px;border-color:transparent}
.lf select{padding-right:34px;cursor:pointer}
.lf-sel{position:relative;color:rgba(232,242,240,.72)}
.lf-sel::after{content:"";position:absolute;right:14px;top:50%;margin-top:-6px;width:7px;height:7px;
  border-right:1.7px solid currentColor;border-bottom:1.7px solid currentColor;
  transform:rotate(45deg);pointer-events:none}
.lf select option{background:#111A20;color:#EAF2F1}
[data-theme=light] .lf-sel{color:var(--body)}
[data-theme=light] .lf select option{background:#fff;color:#14181F}

/* The honeypot must be unreachable, not merely invisible: display:none alone still leaves a field a
   screen reader can announce in some engines, and tabindex=-1 keeps it out of the keyboard path. */
.lf-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.lf-actions{display:flex;flex-wrap:wrap;align-items:center;gap:14px 18px;margin-top:20px}
.lf-submit{display:inline-flex;align-items:center;gap:9px;padding:13px 24px;border-radius:var(--r);
  font-family:var(--sans);font-weight:600;font-size:14.5px;cursor:pointer;
  background:var(--accd,#00615A);color:#fff;border:1px solid rgba(255,255,255,.38);
  transition:transform .2s var(--ease,ease),background-color .2s,border-color .2s}
.lf-submit:hover{transform:translateY(-1px);border-color:rgba(255,255,255,.5)}
.lf-submit:focus-visible{outline:2px solid var(--teal-lit,#19D9C6);outline-offset:3px}
.lf-submit[disabled]{opacity:.6;cursor:progress;transform:none}
.lf-alt{font-size:13.5px;color:rgba(232,242,240,.5)}
.lf-alt a{color:var(--teal-lit,#19D9C6);text-decoration:underline;text-underline-offset:3px}
/* Success and failure are sentences, not codes, and the region is polite so a screen reader hears
   the outcome without the focus being yanked out of the form. */
.lf-note{margin-top:18px;font-size:14.5px;line-height:1.6;color:#EAF2F1}
.lf-note:empty{margin-top:0}
.lf-note.bad{color:#FFCE53}

[data-theme=light] .lf h2{color:var(--head)}
[data-theme=light] .lf .lf-sub{color:var(--body)}
/* NOT --muted: #5E7F8F on white measures 4.28:1, under the 4.5 bar, and these are the smallest
   characters in the form (10.5px uppercase mono). --body on the same ground measures 7.5:1. */
[data-theme=light] .lf label{color:var(--body)}
/* --muted, not --line. --line is a SEPARATOR colour and measures 1.28:1 against the white band
   here, well under the 3:1 WCAG 1.4.11 asks of a control boundary; --muted measures 4.28:1 and is
   already in the palette, so this needs no new hex. ⚠️ The light theme failed this the whole time
   and an earlier measurement of mine reported it PASSING at 15.19:1 -- that reading composited the
   border over the wrong band, on a different page. Measure the band the control actually sits on. */
[data-theme=light] .lf input,[data-theme=light] .lf textarea,[data-theme=light] .lf select{border-color:var(--muted);
  background:var(--card);color:var(--head)}
[data-theme=light] .lf input::placeholder,[data-theme=light] .lf textarea::placeholder{color:var(--muted)}
[data-theme=light] .lf-alt{color:var(--body)}
[data-theme=light] .lf-alt a{color:var(--accd,#00615A)}
[data-theme=light] .lf-note{color:var(--head)}
[data-theme=light] .lf-note.bad{color:#8A5A00}

@media(max-width:520px){
  .lf-field{flex-basis:100%}
}
@media(prefers-reduced-motion:reduce){
  .lf-submit{transition:none}
  .lf-submit:hover{transform:none}
}

/* ---------- skip link ---------- */
/* First focusable element on every page. Invisible until it has keyboard focus, then it sits over
   the nav so a keyboard or screen-reader user can jump past the header in one Tab. z-index clears
   #grain (9000), which is a fixed overlay above everything else. */
.skip{position:absolute;left:12px;top:-64px;z-index:10001;padding:10px 14px;border-radius:var(--rs);
  background:var(--accd,#00615A);color:#fff;font-family:var(--sans);font-weight:600;font-size:14px;
  text-decoration:none;transition:top .15s}
.skip:focus{top:12px;outline:2px solid #fff;outline-offset:2px}
/* [data-theme=light] a in product.css is (0,1,1) and outranked .skip: the label painted accent-on-accent,
   a blank green pill. a.skip ties at (0,1,1) and this file loads later, so it wins. */
a.skip,a.skip:focus,a.skip:hover{color:#fff}
@media(prefers-reduced-motion:reduce){.skip{transition:none}}
