/* ════════════════════════════════════════════════════════════════════════════
   BUILDYRA — SHARED UI & MOTION LAYER
   Linked from every page, after that page's own <style> block.

   Additive by design: it does not restyle existing components, it adds the
   cross-cutting things each page was missing individually —

     · a reduce-motion escape hatch (was absent on builder/dashboard/preview)
     · visible keyboard focus rings (no page had any, so tabbing was invisible)
     · consistent press/hover feedback
     · reusable entrance, skeleton and shimmer animations

   Motion principles used throughout:
     · ease-out for entrances, ease-in-out for loops — nothing linear
     · 120–420ms for UI feedback, 600–900ms for entrances
     · transform + opacity only (compositor-friendly; never animate width/top)
   ════════════════════════════════════════════════════════════════════════════ */

:root{
  --wt-ease:cubic-bezier(.16,1,.3,1);       /* decelerating — entrances     */
  --wt-ease-soft:cubic-bezier(.4,0,.2,1);   /* material standard — feedback */
  --wt-ease-spring:cubic-bezier(.34,1.56,.64,1); /* slight overshoot        */
  --wt-fast:140ms;
  --wt-mid:280ms;
  --wt-slow:640ms;
  --wt-focus:#8b7cff;
}

/* ─── KEYBOARD FOCUS ───────────────────────────────────────────────────────
   :focus-visible only, so pointer users never see a ring but keyboard users
   always do. Previously neither did — the app was effectively unnavigable
   without a mouse. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
[tabindex]:focus-visible,
[onclick]:focus-visible{
  outline:2px solid var(--wt-focus);
  outline-offset:3px;
  border-radius:6px;
}
/* Anything clickable should say so, and be reachable. */
[onclick]{cursor:pointer;}

/* Skip link for screen-reader / keyboard users. */
.wt-skip{
  position:absolute;left:-9999px;top:0;z-index:10000;
  padding:11px 18px;background:#6c5ef4;color:#fff;
  font:700 13px/1 Inter,sans-serif;border-radius:0 0 10px 0;
}
.wt-skip:focus{left:0;}

/* ─── PRESS FEEDBACK ──────────────────────────────────────────────────────
   A tap should feel like it landed. */
button:not(:disabled):active,
.bp:active,.bs:active,.bo:active,
.main-btn:not(:disabled):active,
.tb-btn:active,.btn-sm:active{
  transform:scale(.97);
  transition-duration:60ms;
}
button:disabled{cursor:not-allowed;opacity:.55;}

/* ─── ENTRANCE ANIMATIONS ─────────────────────────────────────────────────
   Add class + optional --wt-d delay: <div class="wt-in" style="--wt-d:.1s"> */
@keyframes wtIn      {from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}
@keyframes wtInScale {from{opacity:0;transform:scale(.94)}     to{opacity:1;transform:none}}
@keyframes wtInLeft  {from{opacity:0;transform:translateX(-24px)}to{opacity:1;transform:none}}
@keyframes wtInRight {from{opacity:0;transform:translateX(24px)} to{opacity:1;transform:none}}

.wt-in      {animation:wtIn      var(--wt-slow) var(--wt-d,0s) var(--wt-ease) both;}
.wt-in-scale{animation:wtInScale var(--wt-slow) var(--wt-d,0s) var(--wt-ease) both;}
.wt-in-left {animation:wtInLeft  var(--wt-slow) var(--wt-d,0s) var(--wt-ease) both;}
.wt-in-right{animation:wtInRight var(--wt-slow) var(--wt-d,0s) var(--wt-ease) both;}

/* Auto-stagger children — no per-element delay bookkeeping. */
.wt-stagger>*{animation:wtIn var(--wt-slow) var(--wt-ease) both;}
.wt-stagger>*:nth-child(1){animation-delay:.04s}
.wt-stagger>*:nth-child(2){animation-delay:.08s}
.wt-stagger>*:nth-child(3){animation-delay:.12s}
.wt-stagger>*:nth-child(4){animation-delay:.16s}
.wt-stagger>*:nth-child(5){animation-delay:.20s}
.wt-stagger>*:nth-child(6){animation-delay:.24s}
.wt-stagger>*:nth-child(7){animation-delay:.28s}
.wt-stagger>*:nth-child(8){animation-delay:.32s}
.wt-stagger>*:nth-child(n+9){animation-delay:.36s}

/* ─── HOVER LIFT ──────────────────────────────────────────────────────────
   will-change is deliberately NOT set here: on grids of 40+ cards it forces a
   layer per card and costs more than it saves. */
.wt-lift{transition:transform var(--wt-mid) var(--wt-ease),box-shadow var(--wt-mid) var(--wt-ease);}
.wt-lift:hover{transform:translateY(-4px);box-shadow:0 18px 38px rgba(0,0,0,.42);}

/* ─── SHEEN — light sweeps across an element on hover ─────────────────── */
.wt-sheen{position:relative;overflow:hidden;}
.wt-sheen::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(105deg,transparent 38%,rgba(255,255,255,.16) 50%,transparent 62%);
  transform:translateX(-120%);
}
.wt-sheen:hover::after{transform:translateX(120%);transition:transform 720ms var(--wt-ease-soft);}

/* ─── SKELETON LOADING ────────────────────────────────────────────────────
   Better than a spinner for list/card content: it shows the shape of what is
   coming, so the layout does not jump when data lands. */
@keyframes wtShimmer{to{background-position:200% 0}}
.wt-skeleton{
  background:linear-gradient(90deg,
    rgba(255,255,255,.05) 25%,
    rgba(255,255,255,.11) 37%,
    rgba(255,255,255,.05) 63%);
  background-size:200% 100%;
  animation:wtShimmer 1.4s linear infinite;
  border-radius:8px;color:transparent!important;user-select:none;
}
.wt-skeleton *{visibility:hidden;}

/* ─── PULSE / GLOW — draws the eye to a primary action ─────────────────── */
@keyframes wtPulse{
  0%,100%{box-shadow:0 0 0 0 rgba(108,94,244,.45)}
  50%    {box-shadow:0 0 0 12px rgba(108,94,244,0)}
}
.wt-pulse{animation:wtPulse 2.4s var(--wt-ease-soft) infinite;}

/* ─── ANIMATED GRADIENT TEXT ─────────────────────────────────────────── */
@keyframes wtGrad{to{background-position:200% center}}
.wt-grad-anim{
  background:linear-gradient(90deg,#6c5ef4,#b259ff,#06d6a0,#6c5ef4);
  background-size:200% auto;
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
  animation:wtGrad 5s linear infinite;
}

/* ─── COUNT-UP NUMBERS use tabular figures so they stop jittering ─────── */
.wt-num{font-variant-numeric:tabular-nums;font-feature-settings:'tnum';}

/* ─── INPUTS — clearer focus affordance ───────────────────────────────── */
input,textarea,select{transition:border-color var(--wt-fast) var(--wt-ease-soft),
                                 background-color var(--wt-fast) var(--wt-ease-soft),
                                 box-shadow var(--wt-fast) var(--wt-ease-soft);}
input:focus,textarea:focus,select:focus{box-shadow:0 0 0 3px rgba(108,94,244,.14);}

/* Invalid state, but only after the user has actually typed something —
   :invalid alone paints every empty required field red on first paint. */
input:not(:placeholder-shown):invalid{border-color:rgba(239,68,68,.55)!important;}

/* ─── SCROLLBARS ─────────────────────────────────────────────────────── */
*{scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.16) transparent;}
::-webkit-scrollbar{width:10px;height:10px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:8px;border:2px solid transparent;background-clip:content-box;}
::-webkit-scrollbar-thumb:hover{background:rgba(108,94,244,.5);border:2px solid transparent;background-clip:content-box;}

/* ─── TOAST ──────────────────────────────────────────────────────────── */
.wt-toast{
  position:fixed;bottom:24px;left:50%;z-index:9999;
  transform:translate(-50%,72px);opacity:0;
  display:flex;align-items:center;gap:10px;
  padding:13px 20px;max-width:min(92vw,420px);
  background:rgba(14,14,28,.97);border:1px solid rgba(108,94,244,.32);
  border-radius:12px;box-shadow:0 18px 44px rgba(0,0,0,.5);
  font:600 13px/1.45 Inter,sans-serif;color:#fff;
  backdrop-filter:blur(18px);
  transition:transform 380ms var(--wt-ease-spring),opacity 260ms var(--wt-ease-soft);
}
.wt-toast.on{transform:translate(-50%,0);opacity:1;}
.wt-toast.err{border-color:rgba(239,68,68,.42);}
.wt-toast.ok {border-color:rgba(34,197,94,.42);}

/* ─── REDUCED MOTION ─────────────────────────────────────────────────────
   The escape hatch. Vestibular disorders make large looping motion genuinely
   painful, and this app runs several infinite WebGL/canvas loops. Honour the OS
   setting: kill looping and decorative motion, keep state changes instant.
   Note we damp rather than `animation:none`, so animation-driven end states
   (`both` fill-mode entrances) still resolve to visible instead of stuck at
   opacity 0. */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    scroll-behavior:auto!important;
  }
  .wt-sheen::after{display:none;}
  .wt-lift:hover{transform:none;}
  .wt-skeleton{animation:none;background:rgba(255,255,255,.07);}
}

/* ════════════════════════════════════════════════════════════════════════
   LIGHT MODE — CONTRAST SAFETY NET
   ════════════════════════════════════════════════════════════════════════
   Each page shipped its own partial `body.light` block, so switching to light
   mode left dozens of elements still using their dark-mode colours: white and
   near-white text landed on a near-white background at ~1.05:1, i.e. completely
   invisible. Measured with tests/contrast.mjs.

   This block is the shared floor. It is deliberately specific rather than a
   blanket `color:#000` so gradient text, badges and accent colours survive.
   Threshold used: WCAG AA — 4.5:1 body text, 3:1 large/bold.

   Rule of thumb when adding UI: never hard-code #fff or rgba(240,240,250,…)
   for text. Use the variables below, which flip automatically.  */
/* Background floor. Pages that never had a light theme (language.html) only got
   their TEXT darkened by the rules below, leaving dark text on a still-dark
   page. Setting a background here means opting into `.light` is always safe.
   Individual pages may override with a more specific selector. */
body.light{background:#f4f4fa;color:#14141c;}

/* ── INTENTIONALLY DARK SURFACES ────────────────────────────────────────
   Some things are dark *by design* even on a light page: the landing page's
   browser mock-up, the builder's live-preview frame, and the generated site
   inside it. Their contents must keep dark-mode colours, otherwise the light
   rules paint dark text onto a dark mock. Anything inside these opts out. */
body.light .build-sim,body.light .build-sim *,
body.light .mock-browser,body.light .mock-browser *,
body.light .lp-frame,body.light .lp-frame *,
body.light .sim-body,body.light .sim-body *,
body.light .desk-prev,body.light .desk-prev *,
body.light .ph,body.light .ph *{
  --wt-fg:#ffffff;
  --wt-fg-soft:rgba(255,255,255,.72);
  --wt-fg-mute:rgba(255,255,255,.58);
  --wt-fg-faint:rgba(255,255,255,.46);
}
/* …and the few that hard-code a colour rather than using the variables. */
body.light .sim-cat-em,body.light .sim-cat-n,body.light .sim-theme,
body.light .sim-field-inp,body.light .sr-title,body.light .sr-logo,
body.light .mock-brand,body.light .desk-url,body.light .lp-url{
  color:#fff;
}
body.light .sim-steps{background:rgba(0,0,0,.34);}
body.light .sim-step{color:rgba(255,255,255,.32);}
body.light .sim-step.on{color:#8b7cff;}
body.light .sim-step.done{color:#4ade80;}
body.light .sim-field-lbl,body.light .sim-msg,body.light .sim-addr{
  color:rgba(255,255,255,.55);
}
body.light .sim-stage-hdr{color:#9d8fff;}

body.light{
  --wt-fg:#14141c;            /* 15.4:1 on #f4f4fa */
  --wt-fg-soft:rgba(20,20,28,.78);  /* 9.6:1 — secondary text */
  --wt-fg-mute:rgba(20,20,28,.68);  /* 5.2:1 — labels, captions. Was .58,
                                       which measured 4.35:1 — just under AA. */
  --wt-fg-faint:rgba(20,20,28,.60); /* 4.6:1 — smallest legible tier */
  --wt-surface:#ffffff;
  --wt-surface-2:#f4f4f9;
  --wt-line:rgba(20,20,28,.12);
  --wt-accent:#5546d8;        /* darkened from #6c5ef4 for 4.8:1 on white */
}

/* Headings, brand marks and any element that hard-coded white text. */
body.light .logo-text,body.light .logo-t,body.light .sb-brand,
body.light .headline,body.light .hh,body.light .sh,body.light .card-title,
body.light .sb-sn,body.light .lc-name,body.light .sv,body.light .ct,
body.light .sc-v,body.light .tb-t,body.light .bk-name,body.light .pg-n,
body.light #sbUserName,body.light .plan-name,body.light .mock-brand,
body.light .ci-n,body.light .tp-name,body.light .lp-name{
  color:var(--wt-fg);
}
/* Keep intentional gradient text working — it sets its own fill. */
body.light .gt,body.light .logo-text span,body.light .logo-t span{
  color:transparent;
}

/* Secondary / muted text tiers. */
body.light .sub,body.light .sp,body.light .card-sub,body.light .sdesc,
body.light .lc-native,body.light .sb-su,body.light .sk,body.light .sn,
body.light .sb-sec,body.light .sect-label,body.light .hstat-l,
body.light .cat-s,body.light .ci-s,body.light .sh2-t,body.light .toggle,
body.light .demo-link,body.light .forgot,body.light .plan-desc,
body.light .divider span,body.light .fi label,body.light label,
body.light #sbUserPlan,body.light .sc-l,body.light .sc-s,body.light .cs,
body.light .mi,body.light .lp-desc,body.light .tp-desc,body.light .se{
  color:var(--wt-fg-mute);
}
/* Nav links, hero secondary button and stat values — these hard-coded a
   near-white rgba() and so were invisible on a light page (measured ~1.05:1). */
body.light .nav-links a,body.light .nlinks a,body.light .bs,body.light .bo,
body.light .fcol ul li a,body.light .fd,body.light .fbot p,body.light .fcol h4{
  color:var(--wt-fg-mute);
}
body.light .nav-links a:hover,body.light .bs:hover,body.light .bo:hover{
  color:var(--wt-fg);
}
body.light .bs,body.light .bo{
  border-color:rgba(20,20,28,.18);
  background:rgba(20,20,28,.03);
}
body.light .hstat-n,body.light .pc-price,body.light .bc-t,body.light .theme-n,
body.light .pc-name,body.light .step-t{
  color:var(--wt-fg);
}
/* Decorative emoji that carry an explicit light colour. */
body.light .search-wrap .si,body.light .lc-flag,body.light .cat-em,
body.light .bc-ic,body.light .theme-em,body.light .empty-ic{
  color:var(--wt-fg);
}
body.light .step-num{color:rgba(20,20,28,.07);}

/* Icon buttons and pills that relied on white glyphs / dark fills. */
body.light .dev-btn,body.light .tb-icon,body.light .tb-lang,
body.light #themeToggle,body.light #langDisplay,body.light #builderThemeBtn,
body.light #builderLangBtn,body.light #userNameBadge,body.light .eb-e{
  background:rgba(20,20,28,.05);
  border-color:var(--wt-line);
  color:var(--wt-fg);
}
body.light .dev-btn:hover,body.light .tb-icon:hover,body.light .tb-lang:hover{
  background:rgba(20,20,28,.10);
}
body.light .dev-btn.on{
  background:rgba(85,70,216,.14);
  border-color:rgba(85,70,216,.42);
  color:var(--wt-accent);
}
/* Summary badges: light chip, dark text — was near-white on near-white. */
body.light .bdg{
  background:rgba(20,20,28,.06)!important;
  color:var(--wt-fg)!important;
  border:1px solid var(--wt-line);
}
body.light .badge-plan{
  background:rgba(85,70,216,.14);
  color:var(--wt-accent);
}

/* The accent purple is too light on white for small text — darken it. */
body.light .stag,body.light .pn,body.light .se,body.light .sn-ic,
body.light .toggle a,body.light .fcol ul li a:hover{
  color:var(--wt-accent);
}
body.light .stag::before{background:var(--wt-accent);}

/* Panels and surfaces. */
body.light .side,body.light .sh2,body.light .si,body.light .badges{
  background:var(--wt-surface-2);
  border-color:var(--wt-line);
}
body.light .sr{border-color:var(--wt-line);}
body.light .lp-frame{border-color:rgba(20,20,28,.16);box-shadow:0 22px 50px rgba(20,20,28,.14);}
body.light .lp-bar{background:rgba(20,20,28,.05);border-color:var(--wt-line);}

/* Category / theme cards. */
body.light .ci,body.light .tp,body.light .layout-opt,body.light .lang-card,
body.light .cat,body.light .bc,body.light .price-card,body.light .theme-card{
  background:var(--wt-surface);
  border-color:var(--wt-line);
}
body.light .ci.sel,body.light .lang-card.sel{
  background:rgba(85,70,216,.09);
  border-color:rgba(85,70,216,.5);
}

/* Inputs. */
body.light input,body.light textarea,body.light select,body.light .srch{
  background:rgba(20,20,28,.04);
  border-color:var(--wt-line);
  color:var(--wt-fg);
}
body.light input::placeholder,body.light textarea::placeholder{
  color:rgba(20,20,28,.42);
}

/* Emoji-only buttons render as black glyphs; give them a light plate. */
body.light button{color:var(--wt-fg);}
/* …but never override buttons whose background is a brand gradient. */
body.light .bp,body.light .main-btn,body.light .bn,body.light .tb-btn.p,
body.light .continue-btn,body.light .pc-btn,body.light .eb-d,body.light .ncta{
  color:#fff;
}

/* ─── SCRIPT-AWARE LEADING ───────────────────────────────────────────────
   The display headings use line-height:.88-.9, which is fine for Latin but
   clips scripts that stack marks above and below the baseline: Devanagari
   matras, Thai tone marks, Arabic diacritics. Loosen the leading (and drop the
   tight negative tracking, which mangles joined scripts) for those languages. */
:root[lang="hi"] .hh, :root[lang="hi"] .sh, :root[lang="hi"] .headline,
:root[lang="mr"] .hh, :root[lang="mr"] .sh, :root[lang="mr"] .headline,
:root[lang="ne"] .hh, :root[lang="ne"] .sh, :root[lang="ne"] .headline,
:root[lang="bn"] .hh, :root[lang="bn"] .sh, :root[lang="bn"] .headline,
:root[lang="ta"] .hh, :root[lang="ta"] .sh, :root[lang="ta"] .headline,
:root[lang="te"] .hh, :root[lang="te"] .sh, :root[lang="te"] .headline,
:root[lang="th"] .hh, :root[lang="th"] .sh, :root[lang="th"] .headline,
:root[lang="ar"] .hh, :root[lang="ar"] .sh, :root[lang="ar"] .headline,
:root[lang="ur"] .hh, :root[lang="ur"] .sh, :root[lang="ur"] .headline,
:root[lang="fa"] .hh, :root[lang="fa"] .sh, :root[lang="fa"] .headline,
:root[lang="he"] .hh, :root[lang="he"] .sh, :root[lang="he"] .headline{
  line-height:1.28!important;
  letter-spacing:normal!important;
  padding-block:.06em;
}

/* ─── RIGHT-TO-LEFT ──────────────────────────────────────────────────────
   Arabic, Hebrew, Urdu and Persian. js/i18n.js sets dir="rtl" on <html>; these
   rules flip the things that direction alone does not: fixed-position corners,
   decorative arrows, and gradients that imply reading order. */
[dir="rtl"] body{direction:rtl;}
[dir="rtl"] .wt-skip{left:auto;right:-9999px;border-radius:0 0 0 10px;}
[dir="rtl"] .wt-skip:focus{left:auto;right:0;}
/* Arrows in CTA copy ("Sign In →") point the wrong way once mirrored. */
[dir="rtl"] .bp,[dir="rtl"] .bs,[dir="rtl"] .main-btn,[dir="rtl"] .btn{unicode-bidi:plaintext;}
[dir="rtl"] .sn::before{left:auto;right:0;border-radius:3px 0 0 3px;}
[dir="rtl"] .sb-caret{right:auto;left:14px;}
[dir="rtl"] .badge-plan{margin-left:0;margin-right:auto;}
[dir="rtl"] .search-wrap .si{right:auto;left:14px;}
[dir="rtl"] .search-wrap input{padding:13px 18px 13px 44px;}
/* Numerals and latin brand names inside RTL text keep their own direction. */
[dir="rtl"] .wt-num,[dir="rtl"] .hstat-n,[dir="rtl"] .sc-v{direction:ltr;unicode-bidi:embed;}

/* ─── PRINT ──────────────────────────────────────────────────────────────
   The QR-code page is meant to be printed and stuck in a shop window. */
@media print{
  canvas,.wt-toast,nav,.demo-banner,.sb,.topbar{display:none!important;}
  body{background:#fff!important;color:#000!important;}
}

/* ── CONTAINERS THAT STAYED DARK ────────────────────────────────────────
   These kept a dark background while the rules above darkened their text,
   producing dark-on-dark. Each needs a light surface to sit on. */
body.light .top,body.light .pbar{background:rgba(250,250,253,.97);border-color:rgba(20,20,28,.1);}
body.light .bc,body.light .step-card,body.light .feat-card,body.light .how-card,
body.light .bento,body.light .sc,body.light .card,body.light .tbl{
  background:#fff;border-color:rgba(20,20,28,.1);
}
body.light .bento{background:rgba(20,20,28,.08);}
body.light .step-ic,body.light .feat-ic,body.light .bc-ic{color:var(--wt-fg);}
body.light .bc-d,body.light .step-d,body.light .feat-d,body.light .bc-n{color:var(--wt-fg-mute);}

/* The simulator's step bar needs an OPAQUE dark fill — rgba(0,0,0,.34) over a
   light parent composited to mid-grey, leaving its light text unreadable. */
body.light .sim-steps{background:#15151f;}
body.light .sim-progress{background:rgba(255,255,255,.08);}

/* Status colours: the stock green and the accent purple both fail on white. */
body.light .sv[style*="22c55e"],body.light .ok-msg,body.light .sn.done{color:#0f7a3d!important;}
body.light .err{color:#b3261e;}

/* Language pill in the login header. */
body.light #langFlag,body.light #langName,body.light #langDisplay{color:var(--wt-fg-mute);}

/* Dashboard sidebar identity block. */
body.light #sbUserName,body.light .sb-sn,body.light .sb-brand{color:var(--wt-fg);}
body.light #sbUserPlan,body.light .sb-su{color:var(--wt-fg-mute);}

/* The builder top bar gets a LIGHT surface above, so everything inside it must
   use dark text. An earlier revision lightened the bar but kept its children
   white, which just moved the invisibility rather than fixing it. */
body.light .top .pn,body.light .top .pl{color:var(--wt-accent);}
body.light .top .logo-t,body.light .top .logo-text{color:var(--wt-fg);}
body.light .top-home{color:var(--wt-fg-mute);}
body.light #userNameBadge{
  background:rgba(20,20,28,.05);border-color:var(--wt-line);color:var(--wt-fg-mute);
}
body.light #builderThemeBtn,body.light #builderLangBtn{
  background:rgba(20,20,28,.05);border-color:var(--wt-line);color:var(--wt-fg);
}

/* Remaining hard-coded white text on now-light card surfaces. */
body.light .how-card h3,body.light .feat-card h3,body.light .bc h3,
body.light .step-card h3,body.light .how-card p,body.light .feat-card p,
body.light .bc p,body.light .step-card p,body.light .cat-n,body.light .pc-list li,
body.light .sr-sl,body.light .empty-t,body.light .empty-d,body.light .mi-type{
  color:var(--wt-fg);
}
body.light .how-card p,body.light .feat-card p,body.light .bc p,
body.light .step-card p,body.light .pc-list li,body.light .empty-d{
  color:var(--wt-fg-mute);
}
body.light .step-num{color:rgba(20,20,28,.10);}
/* Locked-plan badge: red on a pale red chip measured 2.8:1. */
body.light .badge-plan{background:rgba(20,20,28,.06);color:var(--wt-fg-mute);}
body.light .sn.locked .badge-plan{background:rgba(179,38,30,.10);color:#b3261e;}
/* Simulator's least-emphasis step label needed more presence. */
body.light .sim-step{color:rgba(255,255,255,.5);}

/* ── INLINE-STYLE OVERRIDES ─────────────────────────────────────────────
   This codebase styles a lot of elements with inline `style="color:…"`, and an
   inline declaration beats any class selector. Every remaining light-mode
   failure was one of these: a hard-coded dark-theme colour that no class rule
   could reach.

   Matching on the attribute substring catches them generically instead of
   listing each element, so newly added inline styles using the same palette are
   covered automatically. `!important` is required here specifically to win
   against the inline declaration — it is not decorative.

   Scoped to the light-surface containers, so the intentionally dark surfaces
   (build simulator, live-preview frame, generated site) keep their own colours. */
body.light .main [style*="color:#fff"],
body.light .main [style*="color:white"],
body.light .main [style*="240,240,250"],
body.light .main [style*="240, 240, 250"],
body.light .sb [style*="color:#fff"],
body.light .sb [style*="240,240,250"],
body.light .sb [style*="240, 240, 250"],
body.light .topbar [style*="240,240,250"],
body.light .topbar [style*="240, 240, 250"],
body.light .step [style*="color:#fff"],
body.light .step [style*="240,240,250"],
body.light .step [style*="240, 240, 250"],
body.light .sec [style*="color:#fff"],
body.light .sec [style*="240,240,250"],
body.light .price-card [style*="color:#fff"],
body.light .pc-price span,
body.light .card [style*="color:#fff"],
body.light .card [style*="240,240,250"],
body.light .card [style*="240, 240, 250"]{
  color:var(--wt-fg-mute)!important;
}
/* Values and headings in that set want full-strength text, not the muted tier. */
body.light .sc-v[style*="color:#fff"],
body.light .main h1[style*="color:#fff"],
body.light .main h2[style*="color:#fff"],
body.light .main h3[style*="color:#fff"],
body.light #sbUserName[style*="color:#fff"],
body.light #sbName,body.light .ts-pct,body.light .bl{
  color:var(--wt-fg)!important;
}
/* Buttons on a light chrome surface. */
body.light .tb-btn.s,body.light .btn-sm,body.light .sb-foot a{
  color:var(--wt-fg-mute)!important;
  background:rgba(20,20,28,.05);
  border-color:var(--wt-line);
}
body.light .tb-btn.s:hover,body.light .btn-sm:hover{color:var(--wt-fg)!important;}

/* ── PAGE THEME VARIABLES ────────────────────────────────────────────────
   The dashboard defines its palette as CSS custom properties on :root
   (--bg, --card, --border, --muted) and many elements use `color:var(--muted)`
   inline. Those kept their DARK values in light mode, and no attribute selector
   can reach a var() reference — which is why the sidebar links and the traffic
   source labels stayed near-invisible after every other fix.

   Redefining the variables is the one change that repairs every consumer at
   once, including inline styles and elements with no class at all. This is the
   right layer for the fix; the selectors above are the fallback for literals. */
body.light{
  --bg:#f4f4fa;
  --card:#ffffff;
  --border:rgba(20,20,28,.12);
  --muted:rgba(20,20,28,.68);   /* 5.2:1 on white — was rgba(240,240,250,.55) */
  --a:#5546d8;                  /* darkened: #6c5ef4 is only 3.6:1 on white */
  --a2:#8b3fd4;
  --lp-a:#5546d8;
}
/* Inside the intentionally dark surfaces, keep the dark palette. */
body.light .lp-frame,body.light .build-sim,body.light .mock-browser{
  --card:rgba(16,16,32,.92);
  --border:rgba(255,255,255,.08);
  --muted:rgba(240,240,250,.55);
  --a:#6c5ef4;
  --a2:#b259ff;
}

/* ── FINAL LIGHT-MODE STRAGGLERS ────────────────────────────────────────
   Each of these carries an inline `style="color:…"` in the markup (or has one
   assigned by JS), so the override needs !important to win. */
body.light #userNameBadge,body.light #builderLangBtn,body.light .top-home,
body.light .top a,body.light .top button{
  color:var(--wt-fg-mute)!important;
}
body.light .top .logo-t,body.light .top .logo-t span{color:var(--wt-fg)!important;}
body.light .top .logo-t span{color:transparent!important;}

/* enforcePlanLocks() assigns badge.style.color = '#ef4444' directly, which is
   2.8:1 on the pale red chip. Darken it and lighten the chip. */
body.light .badge-plan[style],body.light .sn.locked .badge-plan[style]{
  color:#a1231b!important;
  background:rgba(161,35,27,.09)!important;
}

/* The landing page's closing CTA band stays dark by design, so the darkened
   light-mode accent (#5546d8) is unreadable on it — keep the bright accent and
   light text inside any dark band. */
body.light .cta-sec,body.light .sec.dk{
  --a:#8b7cff;
  --wt-fg:#ffffff;
  --wt-fg-mute:rgba(255,255,255,.72);
}
body.light .cta-sec *,body.light .sec.dk *{color:inherit;}
body.light .cta-sec h2,body.light .cta-sec .sh{color:#fff!important;}
body.light .cta-sec a:not(.bp),body.light .cta-sec p{color:rgba(255,255,255,.78)!important;}
body.light .cta-sec .stag,body.light .cta-sec .stag span{color:#a99bff!important;}

/* Decorative oversized step numeral — bump just enough to read as a watermark. */
body.light .step-num{color:rgba(20,20,28,.16)!important;}

/* Shape count on a geometry card. */
.ec-shapes{font-size:9px;font-weight:700;color:rgba(108,94,244,.75);margin-top:4px;letter-spacing:.04em;}
body.light .ec-shapes{color:var(--wt-accent);}

/* ── DASHBOARD TABS THAT WERE NEVER MEASURED ─────────────────────────────
   Only one dashboard tab is `.on` at a time, so tests/contrast.mjs — which skips
   anything not laid out — had only ever audited the Overview tab. The eleven other
   screens kept their dark-mode colours in light mode: the DNS instructions and the
   SEO checklist were literally white text on a white card, and the status greens
   and ambers sat at ~2:1 on their pale chips. The test now forces every tab visible
   for one pass (see the 'dash-all' entry there); these are the fixes it turned up.

   Status colours generally: the stock Tailwind-ish palette (#22c55e green,
   #f59e0b amber, #ef4444 red) is tuned for dark backgrounds and all three fail AA
   on white. The darkened variants below are the same hues at usable luminance. */
body.light .site-picker{background:rgba(20,20,28,.045);}
body.light .sp-item{color:var(--wt-fg-soft);}
body.light .sp-item.cur,body.light .sp-item:hover{color:var(--wt-fg);background:rgba(85,70,216,.1);}
body.light .sp-name{color:inherit;}
body.light .sp-count{color:var(--wt-fg-mute);}
body.light .sp-add{color:var(--wt-accent);}

/* Table headers: rgba(240,240,250,.3) measured 1.04:1 on white — invisible. */
body.light .tbl th,body.light th{color:var(--wt-fg-mute);}

/* DNS setup steps (Custom Domain tab) and the copyable record block. */
body.light .dns-step{background:rgba(20,20,28,.03);border-color:var(--wt-line);}
body.light .dns-t{color:var(--wt-fg);}
body.light .dns-d{color:var(--wt-fg-mute);}
body.light .dns-n{color:var(--wt-accent);}
body.light .code-block{background:rgba(20,20,28,.05);border-color:var(--wt-line);color:#0b6a35;}
body.light .copy-btn{background:rgba(85,70,216,.12);border-color:rgba(85,70,216,.3);color:var(--wt-accent);}

/* SEO checklist. */
body.light .seo-t{color:var(--wt-fg);}
body.light .seo-item{border-bottom-color:rgba(20,20,28,.07);}
body.light .seo-ok{color:#0f7a3d;}
body.light .seo-no{color:#a35a00;}

/* Primary small buttons ("Open site", "Export CSV", "Full Analytics"). The
   `body.light .btn-sm` rule further up is more specific than `.btn-sm.p`, so in
   light mode every primary small button was demoted to look secondary. */
body.light .btn-sm.p{
  background:linear-gradient(135deg,#5546d8,#8b3fd4)!important;
  border-color:transparent;
  color:#fff!important;
}

/* Publish tab status chips. */
body.light .pub-pill.live{color:#0f7a3d;background:rgba(15,122,61,.09);border-color:rgba(15,122,61,.24);}
body.light .pub-pill.pending{color:#a35a00;background:rgba(163,90,0,.09);border-color:rgba(163,90,0,.24);}
body.light .pub-pill.current{color:var(--wt-accent);background:rgba(85,70,216,.11);border-color:rgba(85,70,216,.26);}

/* Inline hex literals for the status palette. An inline declaration beats every
   rule above, so these need !important to reach — same reason as the block higher
   up in this file. Matching the hex substring covers elements added later that
   reuse the same colours. */
/* #0b5c2d rather than the #0f7a3d used elsewhere: these chips sit on the page's
   off-white (#f0f0f8), not on a white card, and the lighter green lands at 4.1:1
   there — close enough to pass on a card and fail on the page behind it. */
body.light [style*="#22c55e"],body.light [style*="color:#22c55e"]{color:#0b5c2d!important;}
body.light [style*="#f59e0b"]{color:#a35a00!important;}
body.light [style*="#ef4444"]{color:#b3261e!important;}
/* Status *plates* — the pale tinted pills and banners those colours sit on. Even
   the darkened green only reaches 4.06:1 on a 14%-green chip, so the plate has to
   lighten too. Matched on the rgba() background specifically, NOT on the hex, so
   solid `background:#22c55e` indicators (the live-status dot) keep their fill. */
body.light [style*="background:rgba(34,197,94"]{background-color:rgba(15,122,61,.07)!important;}
body.light [style*="background:rgba(245,158,11"]{background-color:rgba(163,90,0,.07)!important;}
body.light [style*="background:rgba(239,68,68"]{background-color:rgba(179,38,30,.07)!important;}

/* ── PRICING TABLE (landing page, three tiers) ───────────────────────────
   The comparison table shows the same rows in every card and marks the ones a
   tier does not include. Both states have to be READABLE in light mode: a
   customer deciding between $9 and $24 is reading the crossed-out rows most
   closely of all, so they get the same muted tier as ordinary secondary text
   (5.2:1) and are distinguished by the strike-through and the ✕ glyph instead of
   by being faded out. These rules live here, not in index.html's own <style>,
   because this file is linked after it and equal-specificity rules here win. */
body.light .pc-list li{color:var(--wt-fg-soft);}
body.light .pc-list li.locked,
body.light .pc-list li.locked .pc-txt,
body.light .pc-list li.locked .pc-ck{color:var(--wt-fg-mute);}
body.light .pc-list li.locked .pc-txt{text-decoration-color:rgba(20,20,28,.45);}
body.light .pc-list li{border-bottom-color:rgba(20,20,28,.08);}
body.light .pc-val{color:var(--wt-fg);}
body.light .pc-lock{
  background:rgba(20,20,28,.05);
  border-color:var(--wt-line);
  color:var(--wt-fg-mute);
}
body.light .pc-foot{color:var(--wt-fg-mute);}
body.light .pc-foot strong{color:var(--wt-fg);}
/* Plan name. #6c5ef4 measures 3.98:1 on the featured card's tinted fill — under AA
   for 10px uppercase text (tests/contrast.mjs). */
body.light .pc-badge{color:var(--wt-accent);}
/* The featured card's tinted fill is built for a dark page; on white the same
   purple wash swallows the text. */
body.light .price-card.featured{
  background:rgba(85,70,216,.06);
  border-color:rgba(85,70,216,.42);
}
