:root{
  --ink:#0B0710;           /* near-black, warm violet undertone */
  --ink-2:#170D22;
  --cream:#F5F1EC;         /* warm off-white, not the old cold cream */
  --cream-dim:rgba(245,241,236,.68);
  /* was .38 — measured contrast on --ink was ~3.2:1, under WCAG AA's 4.5:1 for
     body-sized text (this token is used for real copy: .note, #depth, price
     captions, footer note). .5 measures ~4.8:1, keeps the 3-tier hierarchy
     (full cream > dim > faint) intact, visually still reads as the quietest tier. */
  --cream-faint:rgba(245,241,236,.5);
  --rule:rgba(245,241,236,.18);
  --accent:#FF2D6B;        /* hot crimson-pink — primary energy, CTAs */
  --accent-2:#C6FF4D;      /* acid lime — maximalist pop, secondary */
  --accent-3:#3DD6FF;      /* electric cyan — tech glow, tertiary/AI moments */
  --glass-bg:rgba(245,241,236,.06);
  --glass-border:rgba(245,241,236,.18);
  --ease-snap:cubic-bezier(.16,.84,.32,1);
  --display:"Unbounded","Arial Black",sans-serif;
  --body:"Manrope",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  --mono:"JetBrains Mono","Courier New",monospace;
}
*{box-sizing:border-box}
html{scroll-behavior:auto}
body{
  margin:0;
  background:var(--ink);
  color:var(--cream);
  font-family:var(--body);
  font-weight:400;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
a{color:inherit}
::selection{background:var(--accent); color:var(--ink)}

/* ---------- skip link ---------- */
.skip-link{
  position:fixed; top:-4rem; left:1rem; z-index:100;
  background:var(--accent-2); color:var(--ink); text-decoration:none;
  padding:.85rem 1.4rem; font-family:var(--mono); font-size:.78rem; font-weight:600;
  letter-spacing:.06em; border-radius:2px; transition:top .25s var(--ease-snap);
}
.skip-link:focus{top:1rem}

/* ---------- focus states ----------
   Everything interactive gets an explicit, on-brand ring against --ink —
   browser-default focus outlines are barely visible on a near-black page. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible{
  outline:2px solid var(--accent-2);
  outline-offset:3px;
  border-radius:1px;
}
.cta:focus-visible{outline-offset:5px}
.navtoggle:focus-visible{outline-offset:2px}

/* ---------- WebGL layer ---------- */
#gl{position:fixed; inset:0; width:100%; height:100%; display:block; z-index:0}
body.no-gl #gl{display:none}
body.no-gl{background:radial-gradient(130% 90% at 50% -10%, #2a1233 0%, var(--ink) 55%), var(--ink)}

#grain{
  position:fixed; inset:0; z-index:2; pointer-events:none;
  opacity:.45; mix-blend-mode:overlay;
  background-image:var(--grain);
  background-size:180px 180px;
}
#vignette{
  position:fixed; inset:0; z-index:2; pointer-events:none;
  background:radial-gradient(120% 90% at 50% 50%, transparent 38%, rgba(5,2,8,.76) 100%);
}

/* ---------- top nav ---------- */
#nav{
  position:fixed; top:0; left:0; right:0; z-index:10;
  display:flex; align-items:center; justify-content:space-between;
  padding:1.5rem clamp(1.5rem,5vw,3.5rem);
  background:linear-gradient(to bottom, rgba(11,7,16,.8), transparent);
  pointer-events:none;
  transition:background .3s var(--ease-snap), box-shadow .3s var(--ease-snap), backdrop-filter .3s var(--ease-snap);
}
#nav *{pointer-events:auto}
/* Past a small scroll threshold (toggled in site.js) the nav solidifies into
   a blurred glass bar instead of staying a gradient that fades to fully
   transparent — without this, whatever scrolls up under the transparent
   lower half of the bar visually collides with the logo/links. */
#nav.scrolled{
  background:rgba(9,6,13,.86);
  backdrop-filter:blur(16px) saturate(1.4);
  -webkit-backdrop-filter:blur(16px) saturate(1.4);
  box-shadow:0 1px 0 rgba(245,241,236,.08), 0 16px 32px -20px rgba(0,0,0,.75);
}
/* backdrop-filter on #nav makes it a new CSS containing block for its
   position:fixed .navlinks child the moment the mobile full-screen menu
   overlay is in play — .navlinks's inset:0/translateY(-100%) off-screen
   math then resolves against the slim nav bar's own ~90px height instead
   of the viewport, so the last couple of nav items (language switcher,
   sign-in button) end up rendering just below the bar instead of fully
   hidden. Only triggers once .scrolled is added, which is exactly why this
   was invisible until the page was actually scrolled. A solid near-opaque
   background gets the same "can't see through it" result on this narrow a
   bar without the containing-block side effect, so drop the blur here only.*/
@media (max-width:760px){
  #nav.scrolled{background:rgba(9,6,13,.97); backdrop-filter:none; -webkit-backdrop-filter:none}
}
@media (prefers-reduced-motion:reduce){#nav{transition:none}}
.logo{
  display:inline-flex; align-items:center; gap:.55rem;
  font-family:var(--display); font-weight:700; font-size:1.15rem; letter-spacing:-.01em;
  text-decoration:none; color:var(--cream); text-transform:uppercase;
}
.logo img{width:1.6rem; height:1.6rem; display:block; transform-origin:50% 0%; animation:nav-icon-sway 3.6s ease-in-out infinite}
@keyframes nav-icon-sway{0%,100%{transform:rotate(-4deg)} 50%{transform:rotate(4deg)}}
.navlinks{display:flex; gap:2.2rem; list-style:none; margin:0; padding:0}
.navlinks a{
  position:relative;
  font-size:.7rem; letter-spacing:.14em; text-transform:uppercase; font-weight:600;
  text-decoration:none; color:var(--cream-dim);
  transition:color .3s ease;
  padding-bottom:.3rem;
}
/* Sweeping gradient underline instead of a flat color-swap — the nav's only
   "alive" element besides the ambient canvas behind it, so it's built to be
   noticed without competing with that background. Excluded from .nav-signin,
   which already has its own physical press-motion (see below). */
.navlinks a:not(.nav-signin)::after{
  content:''; position:absolute; left:0; right:0; bottom:0; height:2px;
  background:linear-gradient(90deg, var(--accent), var(--accent-2));
  transform:scaleX(0); transform-origin:left;
  transition:transform .35s var(--ease-snap);
}
.navlinks a:hover,.navlinks a.current{color:var(--accent-2)}
.navlinks a:not(.nav-signin):hover::after,
.navlinks a:not(.nav-signin):focus-visible::after,
.navlinks a.current:not(.nav-signin)::after{transform:scaleX(1)}
.navlinks a.nav-advisory{display:inline-flex; align-items:center; gap:.4rem}
.navlinks a.nav-advisory img{width:.95rem; height:.95rem; display:block; opacity:.85; transform-origin:50% 0%; animation:nav-icon-sway 3.6s ease-in-out infinite}
.navlinks a.nav-advisory:hover img{opacity:1}
@media (prefers-reduced-motion:reduce){
  .logo img, .navlinks a.nav-advisory img{animation:none}
  .navlinks a:not(.nav-signin)::after{transition:none}
}
.navtoggle{display:none}

/* ---------- nav sign-in button ----------
   Was a soft gradient pill with a blurred glow and a diagonal shine
   loop — a generic template pattern lifted from nowhere in particular,
   and the only button on the whole site that didn't match the CTA
   language defined above (solid fill, hard ink border, flat offset
   shadow with zero blur, physical shift on hover). Rebuilt on that
   same system instead of inventing a second one just for the nav.
   Swapped to the signed-in label by assets/account.js on any page
   that loads it and has this ID. */
.navlinks li a.nav-signin{
  background:var(--accent); color:var(--ink);
  padding:.5rem 1.15rem; border-radius:4px; border:2px solid var(--ink);
  font-weight:700; letter-spacing:.06em; text-transform:uppercase; font-size:.74rem;
  box-shadow:3px 3px 0 var(--accent-2);
  transition:transform .16s var(--ease-snap), box-shadow .16s var(--ease-snap);
}
.navlinks li a.nav-signin:hover{
  color:var(--ink); border-color:var(--ink);
  transform:translate(-2px,-2px);
  box-shadow:5px 5px 0 var(--accent-2), 0 0 22px rgba(255,45,107,.4);
}
.navlinks li a.nav-signin.is-signed-in{
  background:rgba(198,255,77,.14); color:var(--accent-2);
  border:1.5px solid var(--accent-2); box-shadow:none; padding:.44rem 1.15rem;
}
.navlinks li a.nav-signin.is-signed-in:hover{background:rgba(198,255,77,.22)}
@media (max-width:760px){
  .navlinks li a.nav-signin{padding:.65rem 1.6rem; font-size:.78rem}
}

/* ---------- language switcher (nav) — one globe button, a dropdown ----------
   Replaces five always-visible text links with a single icon; the five
   options move into a menu that only exists in the DOM's visual flow when
   open. Same open/close/focus-trap shape as .navtoggle, see
   AnchorsEngine._initLangMenu in site.js. */
.lang-switch{position:relative; display:flex; align-items:center}
.globe-btn{
  display:flex; align-items:center; justify-content:center;
  width:2.2rem; height:2.2rem; padding:0; border-radius:50%;
  background:transparent; border:1px solid transparent; cursor:pointer;
  color:var(--accent-2); transition:border-color .25s ease, background .25s ease, transform .25s var(--ease-snap);
}
.globe-btn svg{width:1.15rem; height:1.15rem; display:block}
.globe-btn:hover,.globe-btn[aria-expanded="true"]{
  border-color:var(--glass-border); background:var(--glass-bg); transform:scale(1.08);
}
/* Two-tone accent — lime globe lines, pink meridian — so the icon itself
   carries both brand colours rather than a flat single-colour glyph. */
.globe-btn svg .meridian{stroke:var(--accent)}

.lang-menu{
  position:absolute; top:calc(100% + .7rem); right:0; z-index:20;
  display:flex; flex-direction:column; gap:.1rem;
  min-width:9.5rem; margin:0; padding:.5rem;
  list-style:none; border-radius:12px;
  background:rgba(23,13,34,.92); border:1px solid var(--glass-border);
  backdrop-filter:blur(16px) saturate(1.3); -webkit-backdrop-filter:blur(16px) saturate(1.3);
  box-shadow:0 20px 50px -12px rgba(0,0,0,.8);
  opacity:0; transform:translateY(-6px) scale(.97); pointer-events:none;
  transition:opacity .16s var(--ease-snap), transform .16s var(--ease-snap);
}
.lang-menu.open{opacity:1; transform:translateY(0) scale(1); pointer-events:auto}
.lang-menu a{
  display:block; padding:.55rem .7rem; border-radius:7px;
  font-family:var(--mono); font-size:.74rem; letter-spacing:.04em; font-weight:600;
  text-decoration:none; color:var(--cream-dim); transition:background .2s ease, color .2s ease;
}
.lang-menu a:hover{background:var(--glass-bg); color:var(--accent-2)}
.lang-menu a.current{color:var(--ink); background:var(--accent-2)}
.lang-menu a.current:hover{background:var(--accent-2)}
/* Arabic's RTL flip mirrors the nav (logo/menu swap sides), so the dropdown
   opens from the visual left there instead of trailing off past the edge. */
html[dir="rtl"] .lang-menu{right:auto; left:0}
@media (max-width:760px){
  .globe-btn{width:2.6rem; height:2.6rem}
  .globe-btn svg{width:1.35rem; height:1.35rem}
  .lang-menu{position:static; margin-top:1rem; opacity:1; transform:none; pointer-events:auto;
    background:transparent; border:0; backdrop-filter:none; -webkit-backdrop-filter:none; box-shadow:none;
    align-items:center; display:none;}
  .lang-menu.open{display:flex}
  .lang-menu a{font-size:1rem; text-align:center}
}
@media (max-width:760px){
  .navlinks{
    position:fixed; inset:0; z-index:9; background:var(--ink);
    flex-direction:column; align-items:center; justify-content:center;
    gap:2rem; transform:translateY(-100%); transition:transform .5s cubic-bezier(.6,0,.2,1);
    overflow-y:auto; padding:3rem 0;
  }
  .navlinks.open{transform:translateY(0)}
  .navlinks a{font-size:1.1rem}
  .navtoggle{
    display:block; background:none; border:2px solid var(--rule); color:var(--cream);
    width:2.6rem; height:2.6rem; z-index:11; position:relative;
  }
  /* .lang-switch is a row on desktop because the open menu there is
     position:absolute and doesn't affect layout. On mobile the open menu
     becomes position:static (real content, not an overlay), so a row still
     puts the globe button beside it and vertically centers the short button
     against the now much-taller list — dragging the globe off to one side
     and shoving Tools/Advisory/Log In into new positions the instant it
     opens. That's what actually caused "clicking near Tools changes the
     language": the tap landed where Tools used to be, which the reflow had
     just handed to a language link. Column stacking keeps the whole group
     centered as one unit with no post-open jump. */
  .lang-switch{flex-direction:column; gap:.6rem}
}

/* ---------- footer ---------- */
#site-footer{
  position:relative; z-index:3;
  padding:4rem clamp(1.5rem,5vw,3.5rem) 3rem;
  border-top:2px solid var(--rule);
  display:flex; flex-wrap:wrap; gap:2rem; justify-content:space-between; align-items:flex-end;
  background:var(--ink);
}
#site-footer .fnote{font-size:.78rem; color:var(--cream-faint); max-width:26rem; line-height:1.6}
#site-footer .flinks{display:flex; gap:1.6rem; list-style:none; margin:0; padding:0; align-items:center}
#site-footer .flinks a{font-size:.72rem; letter-spacing:.1em; text-transform:uppercase; font-weight:600; text-decoration:none; color:var(--cream-dim)}
#site-footer .flinks a:hover{color:var(--accent-2)}
/* Contact moved out of the primary nav and lives only here now — styled as a
   real button rather than a plain text link, so it still reads as the site's
   one persistent call to action. */
#site-footer .flinks a.cta-mini{
  padding:.6rem 1.3rem; color:var(--ink); background:var(--accent-2);
  border:2px solid var(--ink); box-shadow:4px 4px 0 var(--accent);
  transition:transform .2s var(--ease-snap), box-shadow .2s var(--ease-snap);
}
#site-footer .flinks a.cta-mini:hover{
  color:var(--ink); transform:translate(-2px,-2px); box-shadow:6px 6px 0 var(--accent);
}

/* ---------- scroll content ---------- */
#scroll{position:relative; z-index:3}
.act{position:relative; min-height:130vh; display:flex; align-items:center; padding:0 clamp(1.5rem,7vw,9rem)}
.act-inner{
  max-width:46rem; will-change:transform,opacity;
  opacity:0; transform:translateY(2.4rem);
  transition:opacity .8s var(--ease-snap), transform .8s var(--ease-snap);
}
.act-inner.in{opacity:1; transform:translateY(0)}
@media (prefers-reduced-motion:reduce){
  .act-inner{opacity:1; transform:none; transition:none}
}
.act.right{justify-content:flex-end}
.act.right .act-inner{text-align:right}
.act.center{justify-content:center}
.act.center .act-inner{text-align:center}
.act.compact{min-height:auto; padding-top:8rem; padding-bottom:6rem}

.eyebrow{
  font-family:var(--mono); font-size:.72rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--accent-2); margin:0 0 1.6rem; font-weight:500; text-shadow:0 2px 18px rgba(5,2,8,.9);
}
.band{
  font-family:var(--mono); font-size:.72rem; letter-spacing:.08em; text-transform:uppercase;
  color:var(--accent-3); margin:0 0 1rem; font-weight:500; text-shadow:0 2px 18px rgba(5,2,8,.9);
}
h1{font-family:var(--display); font-weight:700; font-size:clamp(3rem,11vw,8.4rem); line-height:.94; letter-spacing:-.02em; margin:0 0 2rem; text-wrap:balance; text-shadow:0 4px 40px rgba(5,2,8,.85), 0 1px 4px rgba(5,2,8,.9)}
h2{font-family:var(--display); font-weight:700; font-size:clamp(1.9rem,5vw,3.8rem); line-height:1.04; letter-spacing:-.015em; margin:0 0 1.6rem; text-wrap:balance; text-shadow:0 4px 32px rgba(5,2,8,.85), 0 1px 4px rgba(5,2,8,.9)}
h3{font-family:var(--display); font-weight:600; font-size:clamp(1.25rem,2.4vw,1.7rem); line-height:1.22; margin:0 0 1rem; text-shadow:0 2px 20px rgba(5,2,8,.85)}
.lede{font-size:clamp(1.05rem,1.9vw,1.4rem); line-height:1.6; color:var(--cream-dim); margin:0 0 1.2rem; max-width:34rem; text-shadow:0 2px 20px rgba(5,2,8,.9)}
.act.right .lede{margin-left:auto}
.act.center .lede{margin-left:auto;margin-right:auto}
.note{font-size:.92rem; line-height:1.7; color:var(--cream-faint); margin:2rem 0 0; max-width:30rem; text-shadow:0 2px 16px rgba(5,2,8,.9)}
.act.center .note{margin-left:auto;margin-right:auto}
.rule{width:4.5rem; height:3px; background:var(--accent); margin:0 0 2rem}
.act.right .rule{margin-left:auto}
.act.center .rule{margin-left:auto;margin-right:auto}

/* interactive band marker (click-to-trigger) */
.band-btn{
  display:inline-flex; align-items:center; gap:.6rem;
  background:none; border:2px solid var(--rule); color:var(--cream);
  padding:.6rem 1.15rem; margin-top:1.4rem;
  font-family:var(--body); font-size:.7rem; font-weight:600; letter-spacing:.1em; text-transform:uppercase;
  cursor:pointer; transition:border-color .3s var(--ease-snap), background .3s var(--ease-snap), transform .3s var(--ease-snap);
}
.band-btn:hover,.band-btn:focus-visible{border-color:var(--accent); background:rgba(255,45,107,.1); transform:translateX(3px)}
.band-btn .dot{width:7px; height:7px; border-radius:50%; background:var(--accent-2); box-shadow:0 0 8px var(--accent-2)}

/* detail panel revealed by click-trigger */
.detail{
  max-height:0; overflow:hidden; opacity:0;
  transition:max-height .6s cubic-bezier(.4,0,.2,1), opacity .5s ease;
  margin-top:1.6rem; border-left:3px solid var(--accent); padding-left:1.4rem;
}
.detail.open{max-height:20rem; opacity:1}
.detail p{font-size:.88rem; line-height:1.7; color:var(--cream-dim); margin:0}
/* Gated tool results need more room than an FAQ answer — the grader's full
   breakdown runs to 25 checks across six categories, so this cap has to be
   generous enough that a long result is never silently clipped. */
.detail.tool-result.open{max-height:400rem}

/* ---------- score dial (Grader, Readiness Score) ---------- */
.dial-wrap{position:relative; width:9rem; height:9rem; flex-shrink:0}
.dial-wrap svg{width:100%; height:100%; transform:rotate(-90deg)}
.dial-track{fill:none; stroke:var(--rule); stroke-width:8}
.dial-value{fill:none; stroke:var(--accent-2); stroke-width:8; stroke-linecap:round}
.dial-number{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-family:var(--display); font-weight:700; font-size:2.1rem; color:var(--cream);
}

/* ---------- toggle rows (Package Configurator) ---------- */
.toggle-row{
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding:1rem 0; border-top:1px solid var(--rule); cursor:pointer; user-select:none;
}
.toggle-row:first-child{border-top:none}
.toggle-row .label{font-size:.92rem; color:var(--cream)}
.toggle-row .sub{font-size:.76rem; color:var(--cream-faint); margin-top:.2rem}
.toggle{
  position:relative; width:2.75rem; height:1.5rem; border-radius:99px;
  background:var(--rule); flex-shrink:0; transition:background .3s var(--ease-snap);
}
.toggle::after{
  content:''; position:absolute; top:2px; left:2px; width:1.25rem; height:1.25rem;
  border-radius:50%; background:var(--cream); transition:transform .3s var(--ease-snap);
}
.toggle-row[aria-pressed="true"] .toggle{background:var(--accent-2)}
.toggle-row[aria-pressed="true"] .toggle::after{transform:translateX(1.25rem); background:var(--ink)}
.toggle-row:focus-visible{outline:2px solid var(--accent-2); outline-offset:3px}

/* ---------- jump-nav pills (tools.html) ---------- */
.jump-nav{display:flex; flex-wrap:wrap; gap:.7rem; margin-top:2rem}
.jump-nav a{
  font-family:var(--mono); font-size:.72rem; letter-spacing:.08em; text-transform:uppercase;
  color:var(--cream-dim); text-decoration:none; border:1px solid var(--rule); border-radius:99px;
  padding:.55rem 1.1rem; transition:border-color .3s ease, color .3s ease;
}
.jump-nav a:hover{border-color:var(--accent-2); color:var(--accent-2)}

/* ---------- tool result rows ---------- */
.result-row{display:flex; align-items:baseline; justify-content:space-between; gap:1rem; padding:.7rem 0; border-top:1px solid var(--rule)}
.result-row:first-child{border-top:none}
.result-row .k{font-size:.85rem; color:var(--cream-faint)}
.result-row .v{font-family:var(--display); font-size:1.3rem; color:var(--cream)}
.result-row .v.accent{color:var(--accent-2)}

/* hero */
#hero{min-height:100vh; position:relative}
/* Desktop-only: at 1440x900 the hero eyebrow's top row was overlapping the
   ANCHORS logo/nav links, because the flex-centered .act-inner can start
   above y=0 when its content (the 3-line display heading) is taller than the
   viewport. Scoped to min-width:761px — mobile's hero never had this
   collision, and adding the same padding there was pushing the CTA row down
   into the scroll cue instead (a second bug this created and then fixed
   below). Doesn't change type scale/spacing, just reserves clearance. */
@media (min-width:761px){
  #hero{padding-top:clamp(6rem,14vh,9rem)}
}

/* CTA — hard offset shadow for graphic punch, glow blooms in on hover */
.cta{
  display:inline-block; margin-top:2.4rem; padding:1.1rem 2.6rem;
  border:2px solid var(--ink); color:var(--ink); text-decoration:none;
  background:var(--accent);
  font-size:.76rem; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  box-shadow:6px 6px 0 var(--accent-2);
  transition:transform .3s var(--ease-snap), box-shadow .3s var(--ease-snap);
}
.cta:hover,.cta:focus-visible{transform:translate(-3px,-3px); box-shadow:9px 9px 0 var(--accent-2), 0 0 34px rgba(255,45,107,.4)}
.cta.ghost{border-color:var(--cream); color:var(--cream); background:none; box-shadow:6px 6px 0 var(--rule)}
.cta.ghost:hover{background:var(--cream); color:var(--ink); box-shadow:9px 9px 0 var(--accent)}
.cta.ai{border-color:var(--ink); background:var(--accent-3); color:var(--ink); box-shadow:6px 6px 0 var(--accent)}
.cta.ai:hover{box-shadow:9px 9px 0 var(--accent), 0 0 34px rgba(61,214,255,.45)}
.cta-row{display:flex; gap:1.2rem; flex-wrap:wrap}
.act.center .cta-row{justify-content:center}
.act.right .cta-row{justify-content:flex-end}

/* glass panel */
.glass{
  background:var(--glass-bg); border:2px solid var(--glass-border);
  -webkit-backdrop-filter:blur(18px) saturate(150%); backdrop-filter:blur(18px) saturate(150%);
}
.glow-ring{position:relative}
.glow-ring::before{
  content:''; position:absolute; inset:-2px; border-radius:inherit; z-index:-1;
  background:linear-gradient(135deg, var(--accent), var(--accent-3));
  opacity:.4; filter:blur(16px);
}

/* animated gradient mesh backdrop — three-color, more saturated for the maximalist read */
.mesh{position:absolute; inset:0; z-index:1; pointer-events:none; overflow:hidden}
.mesh::before,.mesh::after{
  content:''; position:absolute; width:60vw; height:60vw; border-radius:50%;
  filter:blur(85px); opacity:.24; mix-blend-mode:screen;
  animation:meshdrift 20s ease-in-out infinite alternate;
}
.mesh::before{background:var(--accent); top:-15%; left:-10%}
.mesh::after{background:var(--accent-3); bottom:-20%; right:-10%; animation-delay:-8s}
@keyframes meshdrift{
  0%{transform:translate(0,0) scale(1)}
  100%{transform:translate(6vw,4vw) scale(1.15)}
}

/* depth gauge */
#gauge{position:fixed; right:clamp(1rem,2.5vw,2.4rem); top:50%; transform:translateY(-50%); z-index:4; display:flex; flex-direction:column; gap:.55rem; align-items:center}
/* Ticks are now real keyboard targets (role=button, tabindex=0 — see site.js).
   The visible mark stays a thin 2px line; a larger invisible hit area sits
   behind it so it's still comfortably clickable/tappable and shows a proper
   focus ring without inflating the visual device. */
#gauge .tick{
  position:relative; width:2px; height:1.6rem; background:var(--rule);
  transition:background .5s ease, height .5s ease; cursor:pointer;
}
#gauge .tick::before{
  content:''; position:absolute; inset:-.6rem -.9rem; /* ~2rem square hit area */
}
#gauge .tick.on{background:var(--accent-2); height:2.4rem; box-shadow:0 0 8px var(--accent-2)}
#gauge .tick:focus-visible{outline:2px solid var(--accent-2); outline-offset:4px}
#depth{position:fixed; left:clamp(1rem,2.5vw,2.4rem); bottom:clamp(1rem,2.5vw,2.2rem); z-index:4; font-family:var(--mono); font-size:.64rem; letter-spacing:.14em; color:var(--cream-faint); font-variant-numeric:tabular-nums}

/* ---------- generic content sections (inner pages) ---------- */
.section{position:relative; z-index:3; padding:7rem clamp(1.5rem,6vw,6rem)}
.section-head{max-width:40rem; margin:0 auto 4rem}
.section-head.left{margin:0 0 4rem}
.grid-3{display:grid; grid-template-columns:repeat(auto-fit,minmax(15rem,1fr)); gap:2rem; max-width:76rem; margin:0 auto}
.card{
  border:2px solid var(--rule); padding:2.2rem; background:rgba(245,241,236,.03);
  opacity:0; transform:translateY(2rem) scale(.97);
  transition:opacity .4s var(--ease-snap), transform .4s var(--ease-snap), border-color .12s var(--ease-snap), box-shadow .12s var(--ease-snap);
}
.card.in{opacity:1; transform:translateY(0) scale(1)}
.card:hover{border-color:var(--accent); transform:translateY(-6px) scale(1.01); box-shadow:9px 9px 0 var(--accent-2); transition-duration:.12s}
.card:active{transform:translateY(-2px) scale(.98); transition-duration:.06s}
.card .num{font-family:var(--mono); font-size:1.5rem; color:var(--accent); margin:0 0 1rem; font-weight:600}

/* Portfolio teaser cards (homepage + Designed by Anchors) — a .card variant
   with a name/tag header and an image, so the sharp hover/reveal motion
   above applies here for free with no extra JS. Header sits ABOVE the image
   (name and category read before the decorative screenshot, which is also
   the right order for a screen reader), matching a reference layout the
   client pointed to: business name + category line, an external-link glyph,
   then the preview — replacing what had drifted into two different, heavier
   mechanisms (a 4-of-10 auto-scrolling rail on the English homepage, a full
   WebGL shader carousel on Designed by Anchors, a THIRD live-iframe rail on
   every translated homepage) with one grid used the same way everywhere. */
.portfolio-card{padding:0; overflow:hidden; display:flex; flex-direction:column}
.portfolio-card .plabel{
  display:flex; align-items:flex-start; justify-content:space-between; gap:.8rem;
  padding:1.2rem 1.3rem 1rem; text-decoration:none; color:inherit;
}
.portfolio-card .pname{display:block; font-family:var(--display); font-weight:700; font-size:1.05rem; margin:0 0 .3rem}
.portfolio-card .ptag{display:block; font-family:var(--mono); font-size:.62rem; letter-spacing:.08em; text-transform:uppercase; color:var(--accent-2)}
.portfolio-card .parrow{
  font-size:1.05rem; color:var(--cream-faint); flex-shrink:0; line-height:1;
  transition:color .3s var(--ease-snap), transform .3s var(--ease-snap);
}
.portfolio-card .plabel:hover .parrow,.portfolio-card .plabel:focus-visible .parrow{color:var(--accent-2); transform:translate(2px,-2px)}
/* .card is now a plain div wrapping two separate interactive children (the
   name link, the preview button) instead of being the link itself, so
   :focus-visible on .card (which matched the old single <a>) no longer fires
   — this restores the same lift/glow feedback for keyboard users tabbing to
   either child. */
.portfolio-card:focus-within{border-color:var(--accent); transform:translateY(-6px) scale(1.01); box-shadow:9px 9px 0 var(--accent-2)}

/* The media zone is a <button>, not part of the name link above: it toggles
   the live site in place (hover on desktop, tap on touch) instead of
   navigating, so "see it move" and "go to it" are two different, honest
   actions rather than one tap doing a surprising thing. height:auto is
   load-bearing — the <img> carries width/height HTML attributes for CLS
   stability, and without an explicit CSS height, that attribute's 495px
   wins over aspect-ratio for the height dimension, so object-fit:cover has
   no room to scale down and renders at 1:1 — a cropped, zoomed-in sliver of
   the source instead of the intended 16:9 frame. That was the actual cause
   of the reported pixelation, on both mobile and desktop; it was never a
   resolution problem. */
.portfolio-card .pmedia{
  all:unset; position:relative; display:block; width:100%; cursor:pointer; overflow:hidden;
}
.portfolio-card .pmedia img{display:block; width:100%; height:auto; aspect-ratio:16/9; object-fit:cover; transition:transform .6s var(--ease-snap)}
.portfolio-card .pmedia:hover img,.portfolio-card .pmedia:focus-visible img{transform:scale(1.05)}
.portfolio-card .pmedia iframe{
  position:absolute; inset:0; width:100%; height:100%; border:0;
  pointer-events:none; opacity:0; transition:opacity .3s var(--ease-snap);
}
.portfolio-card .pmedia iframe.on{opacity:1}
.portfolio-card .pmedia .pplay{
  position:absolute; right:.8rem; bottom:.8rem; z-index:1;
  display:flex; align-items:center; gap:.4rem; padding:.4rem .7rem; border-radius:99px;
  background:rgba(11,7,16,.72); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
  font-family:var(--mono); font-size:.62rem; letter-spacing:.06em; text-transform:uppercase; color:var(--cream);
  opacity:0; transition:opacity .3s var(--ease-snap);
}
.portfolio-card .pmedia:hover .pplay,.portfolio-card .pmedia:focus-visible .pplay{opacity:1}
.portfolio-card .pmedia.is-live .pplay{opacity:0}
@media (prefers-reduced-motion:reduce){
  .portfolio-card .pmedia img{transition:none}
  .portfolio-card .pmedia:hover img{transform:none}
}

/* Exactly two columns regardless of viewport width (down to the mobile
   breakpoint, where a single column is the only usable option) — the client
   asked for all ten stacked 2x5, not an auto-fit grid that would show three
   or four across on a wide monitor. */
.work-grid{display:grid; grid-template-columns:repeat(2,1fr); gap:1.6rem; max-width:64rem; margin:0 auto}
@media (max-width:640px){.work-grid{grid-template-columns:1fr}}

@media (prefers-reduced-motion:reduce){
  .card{opacity:1; transform:none; transition:border-color .12s ease, box-shadow .12s ease}
  .card:hover,.card:active{transform:none}
}

/* pricing card — edgy clipped corner */
.price-card{
  position:relative; border:2px solid var(--rule); padding:2.6rem;
  background:rgba(245,241,236,.03);
  clip-path:polygon(0 0, calc(100% - 2rem) 0, 100% 2rem, 100% 100%, 0 100%);
  transition:border-color .4s var(--ease-snap), transform .4s var(--ease-snap);
}
.price-card.hi{border-color:var(--accent)}
.price-card:hover{transform:translateY(-4px)}
.price-card .tier-name{font-family:var(--mono); font-size:.72rem; letter-spacing:.1em; text-transform:uppercase; color:var(--accent-2); margin:0 0 .8rem}
.price-card .price{font-family:var(--display); font-weight:700; font-size:clamp(2.2rem,4.5vw,3rem); margin:0 0 .3rem; color:var(--cream)}
.price-card .price span{font-family:var(--mono); font-size:.9rem; font-weight:400; color:var(--cream-faint)}
.price-card .for{font-size:.82rem; color:var(--cream-faint); margin:0 0 1.6rem}
.price-card ul{list-style:none; margin:0 0 1.8rem; padding:0}
.price-card li{font-size:.92rem; color:var(--cream-dim); padding:.55rem 0; border-top:1px solid var(--rule)}
.price-card li:first-child{border-top:none}

/* full price table (the-pass.html / the-pass-groups.html) — the setup tier
   cards above sell the shape of the offer; this puts every commitment column
   on screen at once so nobody has to email to find out the real number. The
   wrapper scrolls on its own on narrow screens rather than letting the whole
   page scroll sideways. */
.price-table-wrap{max-width:62rem; margin:0 auto; overflow-x:auto; -webkit-overflow-scrolling:touch}
.price-table{width:100%; border-collapse:collapse; min-width:36rem; font-variant-numeric:tabular-nums}
.price-table caption{
  font-family:var(--mono); font-size:.7rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--accent-2); text-align:start; padding-bottom:1.1rem;
}
.price-table th,.price-table td{padding:.95rem 1rem; text-align:start; border-bottom:1px solid var(--rule)}
.price-table thead th{
  font-family:var(--mono); font-size:.66rem; letter-spacing:.08em; text-transform:uppercase;
  font-weight:500; color:var(--cream-faint); border-bottom:1px solid var(--cream-faint);
}
.price-table tbody th{font-family:var(--mono); font-size:.78rem; font-weight:600; color:var(--cream-dim); white-space:nowrap}
.price-table tbody td{font-family:var(--display); font-weight:700; font-size:1.05rem; color:var(--cream)}
.price-table tbody td.best{color:var(--accent-2)}
.price-table tbody tr:last-child th,.price-table tbody tr:last-child td{border-bottom:none}
.table-note{font-size:.82rem; line-height:1.7; color:var(--cream-faint); max-width:62rem; margin:1.2rem auto 0}

/* ---------- offer pages ----------
   One template for all ten offers. Deliberately spare: no eyebrow furniture,
   no badge, no hero band, no repeated guarantee. The name, the promise, the
   cost of doing nothing, the stack, the terms, one CTA at each end. */
/* the service/product name as an actual tag, not a small caption above the
   headline — this is the thing being sold, so it gets the same pill treatment
   as .building-badge instead of getting buried under the promise. */
.offer-tag{
  display:inline-flex; align-items:center;
  font-family:var(--mono); font-weight:700; font-size:.78rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--ink); background:var(--svc,var(--accent-2)); border-radius:99px;
  padding:.5rem 1.2rem; margin:0 0 1.4rem;
}
.offer-kicker{
  font-family:var(--mono); font-size:.72rem; letter-spacing:.14em; text-transform:uppercase;
  color:var(--svc,var(--accent-2)); margin:0 0 1.1rem;
}
.offer-reassure{font-family:var(--mono); font-size:.78rem; color:var(--cream-faint); margin:.9rem 0 0}
.offer-synth{
  font-family:var(--display); font-weight:700; font-size:clamp(1.1rem,2.2vw,1.4rem);
  color:var(--cream); max-width:36rem; margin:2rem 0 0; line-height:1.4;
}
.offer-h1{
  font-family:var(--display); font-weight:800; letter-spacing:-.025em; line-height:1.02;
  font-size:clamp(2.1rem,6vw,4.4rem); margin:0 0 1.2rem; color:var(--cream);
}
.offer-sub{font-size:clamp(1.05rem,2vw,1.35rem); line-height:1.55; color:var(--cream-dim); margin:0 0 2.2rem; max-width:34rem}
.offer-h2{
  font-family:var(--display); font-weight:700; letter-spacing:-.015em;
  font-size:clamp(1.5rem,3vw,2.1rem); margin:0 0 2rem; color:var(--cream);
}
/* the quiet-cost paragraph — set large, alone, with the accent as a spine.
   It has to be read as a thought, not skimmed as body copy. */
.offer-cost{
  font-size:clamp(1.15rem,2.3vw,1.65rem); line-height:1.5; color:var(--cream);
  max-width:40rem; margin:0; padding-inline-start:1.6rem;
  border-inline-start:3px solid var(--svc,var(--accent));
}
.offer-terms{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));
  gap:1.6rem 3rem; max-width:44rem; margin:0 0 2rem;
}

/* offer pages (restgo / the-pass / the-pass-groups): the "what you get" stack
   was seven identical grey cards. The numeral becomes the graphic element the
   rest of the site already leans on, and the card carries the page's accent. */
.stack-grid .card{--svc:var(--accent-2); position:relative; overflow:hidden}
.stack-grid .card .num{
  font-family:var(--display); font-weight:800; font-size:2.6rem; line-height:1;
  color:transparent; -webkit-text-stroke:1.5px var(--svc); margin:0 0 .9rem;
}
.stack-grid .card:hover{border-color:var(--svc); box-shadow:9px 9px 0 var(--svc)}
.stack-grid .card h3{letter-spacing:-.01em}

/* the commitment promise, set apart — it is the one claim on these pages that
   carries a cost to us if we miss it, so it gets a border instead of being
   buried in a paragraph. Used in the hero and repeated above the closing CTA. */
.commit-block{
  border:1px solid var(--accent-2); border-radius:.9rem; padding:1.1rem 1.4rem;
  max-width:34rem; margin:1.8rem 0; font-size:.92rem; line-height:1.65; color:var(--cream);
}
.act.center .commit-block,.section-head .commit-block{margin-left:auto; margin-right:auto}

/* "actively building" badge (products.html) — a live pulse reads as more
   compelling than a static "coming soon", the same open-loop principle
   as everywhere else on this site that shows work in progress. */
.building-badge{
  display:inline-flex; align-items:center; gap:.5rem;
  font-family:var(--mono); font-size:.66rem; letter-spacing:.1em; text-transform:uppercase;
  color:var(--accent-3); border:1px solid var(--accent-3); border-radius:99px;
  padding:.4rem 1rem; margin-bottom:1.2rem;
}
.building-badge .pulse{width:.4rem; height:.4rem; border-radius:99px; background:var(--accent-3); animation:building-pulse 1.6s ease-in-out infinite; flex:0 0 auto}
@keyframes building-pulse{0%,100%{opacity:.35} 50%{opacity:1}}
@media (prefers-reduced-motion:reduce){.building-badge .pulse{animation:none}}

.stat-row{display:flex; flex-wrap:wrap; gap:3rem; max-width:60rem; margin:0 auto}
.stat{min-width:9rem}
.stat .n{font-family:var(--display); font-weight:700; font-size:clamp(2.2rem,5vw,3.4rem); line-height:1}
.stat .l{font-family:var(--mono); font-size:.72rem; letter-spacing:.1em; text-transform:uppercase; color:var(--cream-faint); margin-top:.6rem}

/* forms (contact / lead magnet) */
.field{margin-bottom:1.4rem; max-width:32rem}
.field.wide{max-width:none}
.field label{display:block; font-family:var(--mono); font-size:.7rem; letter-spacing:.1em; text-transform:uppercase; color:var(--accent-2); margin-bottom:.6rem}
.field input,.field textarea,.field select{
  width:100%; background:transparent; border:none; border-bottom:2px solid var(--rule);
  color:var(--cream); font-family:var(--body); font-size:1rem; padding:.7rem 0;
  outline:none; transition:border-color .3s ease;
}
.field input:focus,.field textarea:focus,.field select:focus{border-color:var(--accent)}
.field textarea{resize:vertical; min-height:6rem}
.field select{cursor:pointer}
.field select option{background:var(--ink); color:var(--cream)}

/* live validation — a field is marked .err on blur (empty-when-required or a
   failed pattern) by AnchorsForms.enhance(), not by relying on the browser's
   own mismatched-looking native bubble, which breaks position inside a long
   scrolling form and can't be styled to match. required stays in the markup
   as a real fallback for a no-JS submit. */
.field.err input,.field.err textarea,.field.err select{border-color:#FF6B8B}
.field-msg{display:none; font-size:.74rem; color:#FF6B8B; margin-top:.5rem; font-family:var(--mono); letter-spacing:.02em}
.field.err .field-msg{display:block}
.consent.err label{color:#FF6B8B}

/* submit-button loading state — swaps the label for a spinner and blocks
   re-clicks for the round trip, so a slow connection can't produce two
   emails/codes/applications from one impatient extra click. */
.cta[disabled]{opacity:.55; cursor:not-allowed; pointer-events:none}
.cta.is-loading{position:relative; color:transparent !important}
.cta.is-loading::after{
  content:''; position:absolute; left:50%; top:50%; width:1.05rem; height:1.05rem;
  margin:-.52rem 0 0 -.52rem; border-radius:50%; border:2px solid rgba(11,7,16,.3);
  border-top-color:var(--ink); animation:cta-spin .7s linear infinite;
}
.cta.ghost.is-loading::after{border-color:rgba(245,241,236,.3); border-top-color:var(--cream)}
@keyframes cta-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.cta.is-loading::after{animation-duration:1.4s}}

/* a visible cooldown on "Resend" — without it a visitor who doesn't see the
   first code lands on the server's rate_limited error by clicking twice in
   two seconds, which reads as a bug rather than the safeguard it is. */
.resend-cooldown{opacity:.5; cursor:not-allowed; pointer-events:none}

/* long-form scroll progress (apply.html) — a thin fill tracking how far
   through the form's own scroll height the viewport has reached, so a
   32-field application signals "you're most of the way there" instead of
   scrolling into a void with no sense of how much is left. */
.form-progress{position:sticky; top:5.4rem; z-index:5; height:3px; background:var(--rule); margin-bottom:1px}
.form-progress-fill{height:100%; width:0%; background:var(--accent-2); transition:width .1s linear}

/* the post-submit state for a form that succeeds — the panel itself is
   replaced by this rather than only appending a status line, so "it worked"
   reads as a real confirmation, not a line of fine print under a button
   that's still sitting there inviting a second click. */
.form-success{display:flex; align-items:flex-start; gap:1.1rem; padding:1rem 0}
.form-success .fs-icon{
  width:2.8rem; height:2.8rem; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:rgba(198,255,77,.12); border:1px solid var(--accent-2); color:var(--accent-2); flex:0 0 auto;
}
.form-success .fs-icon svg{width:1.4rem; height:1.4rem}
.form-success h3{margin:0 0 .5rem; font-family:var(--display); font-weight:700; font-size:1.25rem; color:var(--cream)}
.form-success p{margin:0; color:var(--cream-dim); font-size:.9rem; line-height:1.6; max-width:30rem}

/* inline "what's this?" glossary toggle next to a jargon-heavy field label —
   reuses .band-btn so AnchorsEngine's existing click-to-toggle wiring picks
   it up for free, this class just strips the full-size button chrome down
   to an inline underlined word. */
.term-help{
  display:inline; background:none; border:none; padding:0; margin-inline-start:.5rem;
  font-family:var(--mono); font-size:.66rem; letter-spacing:.03em; text-transform:none;
  font-weight:600; color:var(--accent-2); cursor:pointer;
  text-decoration:underline; text-underline-offset:3px; transform:none;
}
.term-help:hover,.term-help:focus-visible{color:var(--accent); background:none; transform:none}
.field .detail{margin-top:.7rem; margin-bottom:.2rem}

/* legal consent row (funding application) */
.consent{display:flex; align-items:flex-start; gap:.8rem; max-width:34rem; margin:1.8rem 0}
.consent input[type=checkbox]{
  flex:0 0 auto; width:1.15rem; height:1.15rem; margin-top:.15rem;
  accent-color:var(--accent); cursor:pointer;
}
.consent label{font-size:.86rem; line-height:1.6; color:var(--cream-dim); cursor:pointer}
.consent a{color:var(--accent-2)}

.status-line{font-family:var(--mono); font-size:.8rem; color:var(--cream-faint); margin-top:1rem; min-height:1.4em}

/* ---------- client portal: the dashboard (account.html, signed in) ----------
   Two very different jobs share #account-panel: a 32rem login card, and — for
   an actual paying client with rows in `projects` — a wide engagement portal.
   .mode-dashboard.has-projects is the switch; a free-tool visitor with no
   projects stays in the narrower shell and just sees saved calculator
   results, so the width change never appears to someone it isn't built for. */
#account-panel{max-width:32rem; margin:0 auto; transition:max-width .3s var(--ease-snap)}
#account-panel.is-dashboard{max-width:min(78rem,94vw)}

/* LinkedIn-style shell: a persistent left profile+nav rail beside a
   scrollable main column, rather than the account.js of old (everything —
   login card and dashboard alike — squeezed into one narrow box). Nav
   links are same-page anchor jumps (JS-driven scrollIntoView, not native
   href jump — see account.html — so the fixed site nav's height can be
   accounted for via scroll-margin-top below without fighting it). */
.dash-layout{display:grid; grid-template-columns:16.5rem 1fr; gap:2.6rem; align-items:start}
.dash-sidebar{position:sticky; top:6.5rem; display:flex; flex-direction:column; gap:1.8rem}
.dash-main{min-width:0}
.dash-main > section{scroll-margin-top:6.5rem}
.dash-main > section + section{margin-top:2.6rem; padding-top:2.6rem; border-top:1px solid var(--rule)}

.dash-profile-card{text-align:left}
.dash-avatar{
  width:3.4rem; height:3.4rem; border-radius:50%; display:flex; align-items:center; justify-content:center;
  font-family:var(--display); font-weight:800; font-size:1.2rem; color:var(--ink);
  background:linear-gradient(135deg, var(--accent-2), var(--accent-3)); margin:0 0 1rem;
}
.dash-profile-card h2{
  font-family:var(--display); font-weight:800; letter-spacing:-.02em; line-height:1.15;
  font-size:1.25rem; margin:0 0 .5rem; color:var(--cream);
}
.dash-type-badge{
  display:inline-flex; align-items:center; gap:.4rem; font-family:var(--mono); font-size:.62rem;
  letter-spacing:.08em; text-transform:uppercase; color:var(--accent-2); border:1px solid var(--accent-2);
  border-radius:99px; padding:.28rem .7rem; margin:0 0 .8rem;
}
.dash-profile-card .dash-sub{color:var(--cream-faint); font-size:.82rem; line-height:1.5; margin:0}

.dash-nav{display:flex; flex-direction:column; gap:.15rem; padding:1.3rem 0; border-top:1px solid var(--rule); border-bottom:1px solid var(--rule)}
.dash-nav-link{
  font-family:var(--mono); font-size:.76rem; letter-spacing:.03em; color:var(--cream-dim); text-decoration:none;
  padding:.55rem .6rem; border-radius:.5rem; transition:background .15s ease, color .15s ease; cursor:pointer;
  border:none; background:none; text-align:left; display:block; width:100%;
}
.dash-nav-link:hover{color:var(--cream); background:rgba(245,241,236,.05)}
.dash-nav-link.is-active{color:var(--accent-2); background:rgba(198,255,77,.08); font-weight:600}

.dash-quick-actions{display:flex; flex-direction:column; gap:.6rem}
.dash-quick-heading{font-family:var(--mono); font-size:.62rem; letter-spacing:.1em; text-transform:uppercase; color:var(--cream-faint); margin:0 0 .3rem}
.dash-quick-actions a{color:var(--cream-dim); font-size:.82rem; text-decoration:none; transition:color .15s ease}
.dash-quick-actions a:hover{color:var(--accent-2)}

/* Recent Activity — synthesized client-side from the same projects payload
   (milestones/deliverables/invoices each now carry a real timestamp) rather
   than a dedicated activity-log table — a feed of what already happened,
   not a new thing to track server-side. */
.dash-activity-list{list-style:none; margin:1.5rem 0 0; padding:0; display:flex; flex-direction:column; gap:1rem}
.dash-activity-item{display:flex; align-items:flex-start; gap:.9rem}
.dash-activity-icon{
  flex:0 0 auto; width:2.1rem; height:2.1rem; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:rgba(245,241,236,.06); color:var(--accent-2);
}
.dash-activity-icon svg{width:1.05rem; height:1.05rem}
.dash-activity-body p{margin:0; font-size:.88rem; color:var(--cream-dim); line-height:1.5}
.dash-activity-body strong{color:var(--cream); font-weight:600}
.dash-activity-body .dash-activity-when{font-family:var(--mono); font-size:.66rem; color:var(--cream-faint); margin-top:.25rem}

@media (max-width:900px){
  .dash-layout{grid-template-columns:1fr}
  .dash-sidebar{position:static; flex-direction:row; flex-wrap:wrap; align-items:center; gap:1.4rem 2rem}
  .dash-profile-card{flex:1 1 auto; min-width:12rem}
  .dash-nav{flex-direction:row; flex-wrap:wrap; border:none; padding:0; gap:.4rem}
  .dash-nav-link{padding:.4rem .7rem; border:1px solid var(--rule)}
  .dash-quick-actions{flex-direction:row; flex-wrap:wrap; gap:1.2rem}
  .dash-quick-heading{display:none}
}

.dash-stats{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(11rem,1fr)); gap:1px;
  background:var(--rule); border:1px solid var(--rule); border-radius:.9rem; overflow:hidden;
  margin:0 0 2.2rem;
}
.stat-tile{background:rgba(11,7,16,.55); padding:1.2rem 1.3rem}
.stat-tile .stat-k{font-family:var(--mono); font-size:.64rem; letter-spacing:.1em; text-transform:uppercase; color:var(--cream-faint); margin:0 0 .5rem}
.stat-tile .stat-v{font-family:var(--display); font-weight:800; font-size:1.5rem; letter-spacing:-.01em; color:var(--cream); line-height:1.1}
.stat-tile .stat-v small{font-family:var(--mono); font-size:.62rem; font-weight:500; letter-spacing:.04em; color:var(--cream-faint); text-transform:none}
.stat-tile.accent .stat-v{color:var(--accent-2)}

.projects-heading{font-family:var(--mono); font-size:.72rem; letter-spacing:.12em; text-transform:uppercase; color:var(--accent-2); margin:0 0 1.2rem}

.eng-card{
  --svc:var(--accent-2); position:relative; border:1px solid var(--rule); border-radius:1.1rem;
  padding:1.7rem 1.8rem 1.9rem; margin-bottom:1.6rem; background:rgba(245,241,236,.025);
  border-top:2px solid var(--svc); overflow:hidden;
}
.eng-head{display:flex; align-items:flex-start; justify-content:space-between; gap:1rem; flex-wrap:wrap}
.eng-head h3{margin:0; font-family:var(--display); font-weight:700; font-size:1.2rem; color:var(--cream)}
.eng-notes{color:var(--cream-dim); font-size:.88rem; line-height:1.6; margin:.7rem 0 0; max-width:42rem}
.eng-progress-wrap{display:flex; align-items:center; gap:.6rem; margin-top:.9rem}
.eng-progress-track{flex:1; max-width:14rem; height:5px; border-radius:99px; background:var(--rule); overflow:hidden}
.eng-progress-fill{height:100%; border-radius:99px; background:var(--svc); transition:width .5s var(--ease-snap)}
.eng-progress-pct{font-family:var(--mono); font-size:.68rem; color:var(--cream-faint); white-space:nowrap}

.status-badge{
  font-family:var(--mono); font-size:.62rem; letter-spacing:.08em; text-transform:uppercase;
  padding:.28rem .7rem; border-radius:99px; white-space:nowrap; border:1px solid var(--rule); color:var(--cream-dim);
  flex:0 0 auto;
}
.status-badge.status-in_progress{color:var(--accent-3); border-color:var(--accent-3)}
.status-badge.status-review{color:var(--accent); border-color:var(--accent)}
.status-badge.status-live,.status-badge.status-paid,.status-badge.status-done{color:var(--accent-2); border-color:var(--accent-2)}
.status-badge.status-overdue{color:var(--accent); border-color:var(--accent); background:rgba(255,45,107,.1)}
.status-badge.status-on_hold{color:var(--cream-faint); border-color:var(--rule)}

/* vertical connected-dot timeline — reads the same at any milestone count,
   desktop or mobile, so there's no separate cramped layout to maintain */
.mstone-timeline{list-style:none; margin:1.5rem 0 0; padding:0}
.mstone-timeline li{position:relative; padding:0 0 1.3rem 1.7rem}
.mstone-timeline li:last-child{padding-bottom:0}
.mstone-timeline li::before{
  content:''; position:absolute; left:.3rem; top:.3rem; bottom:-.2rem; width:1px; background:var(--rule);
}
.mstone-timeline li:last-child::before{display:none}
.mstone-timeline .m-dot{
  position:absolute; left:0; top:.15rem; width:.65rem; height:.65rem; border-radius:99px;
  background:var(--rule); border:2px solid var(--bg,#0B0710); box-shadow:0 0 0 1px var(--rule);
}
.mstone-timeline li.m-done .m-dot{background:var(--svc); box-shadow:0 0 0 1px var(--svc)}
.mstone-timeline li.m-in_progress .m-dot{background:var(--accent-3); box-shadow:0 0 0 1px var(--accent-3)}
.mstone-timeline .m-row{display:flex; align-items:baseline; justify-content:space-between; gap:1rem; flex-wrap:wrap}
.mstone-timeline .m-title{font-size:.92rem; color:var(--cream-dim)}
.mstone-timeline li.m-done .m-title{color:var(--cream-faint); text-decoration:line-through}
.mstone-timeline li.m-in_progress .m-title{color:var(--cream); font-weight:600}
.mstone-timeline .m-due{font-family:var(--mono); font-size:.68rem; color:var(--cream-faint); white-space:nowrap}

.deliverable-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(13rem,1fr)); gap:.8rem; margin-top:1.6rem}
.deliverable-tile{
  display:flex; align-items:center; gap:.7rem; padding:.8rem .9rem; border:1px solid var(--rule);
  border-radius:.7rem; text-decoration:none; color:inherit; transition:border-color .2s ease, transform .2s ease;
}
.deliverable-tile:hover,.deliverable-tile:focus-visible{border-color:var(--svc); transform:translateY(-2px)}
.deliverable-tile svg{flex:0 0 auto; width:1.3rem; height:1.3rem; color:var(--svc)}
.deliverable-tile .d-name{font-size:.84rem; color:var(--cream-dim); line-height:1.35}
.deliverable-tile:hover .d-name{color:var(--cream)}

.invoice-table{margin-top:1.6rem; border-top:1px solid var(--rule)}
.invoice-row{
  display:grid; grid-template-columns:1fr auto auto auto; align-items:center; gap:1rem;
  font-size:.85rem; padding:.75rem 0; border-bottom:1px solid var(--rule);
}
.invoice-row .i-desc{color:var(--cream-dim)}
.invoice-row .i-due{font-family:var(--mono); font-size:.68rem; color:var(--cream-faint)}
.invoice-row .i-amt{font-family:var(--display); font-weight:700; color:var(--cream)}
.invoice-row a{color:var(--svc); text-decoration:none; font-size:.78rem; white-space:nowrap; font-weight:600}
.invoice-row a:hover{text-decoration:underline}

.results-panel{margin-top:.6rem}
.dash-settings-body{max-width:24rem}

@media (max-width:640px){
  .invoice-row{grid-template-columns:1fr auto; row-gap:.3rem}
  .invoice-row .i-due{order:3}
}

/* "I am a..." signup picker — three selectable cards standing in for a
   radio group (the native inputs are visually hidden, not display:none, so
   they stay in the tab order and keyboard/AT users get real radio
   semantics). :has() drives the selected look; the JS also toggles
   .is-selected as a belt-and-braces fallback for the rare browser without
   :has() support, so the feature degrades to "still shows a clear
   selection" rather than "looks unselected forever." */
.account-type-picker{display:grid; grid-template-columns:repeat(3,1fr); gap:.7rem; margin-top:.3rem}
.account-type-option{
  position:relative; display:flex; flex-direction:column; gap:.5rem; padding:1rem .9rem;
  border:1px solid var(--rule); border-radius:.8rem; cursor:pointer;
  transition:border-color .2s ease, background .2s ease; -webkit-tap-highlight-color:transparent;
}
.account-type-option input{position:absolute; opacity:0; width:1px; height:1px; pointer-events:none}
.account-type-option svg{width:1.3rem; height:1.3rem; color:var(--cream-faint); transition:color .2s ease}
.account-type-option .ato-name{font-family:var(--display); font-weight:700; font-size:.86rem; color:var(--cream)}
.account-type-option .ato-desc{font-size:.72rem; line-height:1.4; color:var(--cream-faint)}
.account-type-option:hover{border-color:var(--cream-faint)}
.account-type-option.is-selected,.account-type-option:has(input:checked){
  border-color:var(--accent-2); background:rgba(198,255,77,.06);
}
.account-type-option.is-selected svg,.account-type-option:has(input:checked) svg{color:var(--accent-2)}
.account-type-option:has(input:focus-visible){outline:2px solid var(--accent-2); outline-offset:2px}
.field.err .account-type-option{border-color:#FF6B8B}
@media (max-width:560px){.account-type-picker{grid-template-columns:1fr}}

/* ---------- account page: log in / create account switcher ----------
   Both paths hit the same passwordless email-code endpoint underneath
   (the backend already no-ops a blank name/company against an existing
   user — see anchors_find_or_create_user in api/auth.php) — this is a
   front-end-only choice so a returning visitor isn't asked for a name
   and company they already gave us the first time. */
.auth-tabs{display:flex; border-bottom:1px solid var(--rule); margin-bottom:1.8rem}
.auth-tab{
  flex:1; background:none; border:none; padding:0 0 .9rem; cursor:pointer;
  font-family:var(--mono); font-size:.76rem; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  color:var(--cream-faint); border-bottom:2px solid transparent; margin-bottom:-1px;
  transition:color .2s ease, border-color .2s ease;
}
.auth-tab:hover{color:var(--cream-dim)}
.auth-tab.is-active{color:var(--accent-2); border-color:var(--accent-2)}
.signup-only{display:none}
.mode-signup .signup-only{display:block}
.status-line.err{color:#FF6B8B}
.status-line.ok{color:var(--accent-2)}

@media (max-width:640px){
  #gauge{display:none}
  .act{min-height:110vh}
  .stat-row{gap:1.8rem}
}
@media (prefers-reduced-motion:reduce){
  .act-inner{transition:none !important}
}

/* ---------- kinetic type (scroll-velocity skew, applied via JS) ---------- */
.kinetic{display:inline-block; will-change:transform; transform-origin:50% 50%}

/* ---------- marquee ticker ---------- */
.marquee{
  overflow:hidden; position:relative; z-index:3;
  border-top:2px solid var(--rule); border-bottom:2px solid var(--rule);
  padding:1.15rem 0; background:var(--ink-2);
}
.marquee-track{display:flex; width:max-content; animation:marquee-scroll 26s linear infinite}
.marquee-track .group{display:flex; align-items:center}
.marquee-track span{
  display:inline-block; padding:0 1.6rem; font-family:var(--mono); font-size:.85rem;
  letter-spacing:.08em; text-transform:uppercase; color:var(--cream-dim); white-space:nowrap;
}
.marquee-track span.pop{color:var(--accent-2)}
.marquee-track span.sep{color:var(--accent); padding:0 .4rem}
@keyframes marquee-scroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){.marquee-track{animation:none}}

/* ---------- ghost numerals (background graphic device) ---------- */
.ghost-num{
  position:absolute; font-family:var(--display); font-weight:800;
  font-size:clamp(9rem,24vw,22rem); line-height:1; color:transparent;
  -webkit-text-stroke:1.5px var(--rule); z-index:0; pointer-events:none; user-select:none;
}

/* ---------- diagonal section cuts (used sparingly) ---------- */
.section.cut-top{clip-path:polygon(0 5vw,100% 0,100% 100%,0 100%); padding-top:10rem}
.section.cut-bottom{clip-path:polygon(0 0,100% 0,100% calc(100% - 5vw),0 100%); padding-bottom:10rem}


/* ---------- portfolio rail — coverflow tiles with live site embeds ----------
   Used on the home page and on designed-by-anchors.html. See assets/rail.js;
   several rules here are relied on by that script and are noted where so. */

.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

.rail-wrap{position:relative}

/* The pause control. WCAG 2.2.2 needs auto-moving content to be stoppable by a
   real control — hover-pause is no use to a keyboard or touch visitor — and it
   sits before the rail in source order so it's reachable first. 44px min target. */
.rail-toggle{
  display:inline-flex; align-items:center; justify-content:center; gap:.5rem;
  min-width:44px; min-height:44px; padding:.4rem .9rem;
  background:var(--glass-bg); border:1px solid var(--glass-border); border-radius:99px;
  color:var(--cream-dim); font-family:var(--mono); font-size:.66rem;
  letter-spacing:.12em; text-transform:uppercase; cursor:pointer;
  transition:color .3s ease, border-color .3s ease;
}
.rail-toggle:hover{color:var(--accent-2); border-color:var(--accent-2)}
.rail-toggle::before{content:"❙❙"; font-size:.7em; letter-spacing:-.1em}
.rail-toggle[data-state="paused"]::before{content:"▶"; letter-spacing:0}

.work-rail{
  display:flex;
  gap:1.4rem;
  list-style:none;
  margin:1rem 0 0;
  overflow-x:auto;
  overscroll-behavior-x:contain;   /* stops trackpad overscroll firing browser back-nav */
  -webkit-overflow-scrolling:touch;
  /* Vertical padding is load-bearing twice over: it's the headroom the centre
     tile's focus ring needs, and without it overflow-x:auto's implicit
     overflow-y:auto clips the ring and spawns a phantom vertical scrollbar.
     (The scale itself peaks at exactly 1.0, so tiles never exceed layout size.) */
  padding:2.4rem clamp(1.2rem,6vw,5rem) 2.8rem;
  margin-inline:calc(-1 * clamp(1.2rem,6vw,5rem));
  scrollbar-width:thin;
  scrollbar-color:var(--rule) transparent;
  /* Deliberately no scroll-snap: it fights the continuous scale and, on iOS,
     ping-pongs against programmatic scrolling during momentum. */
}
.work-rail::-webkit-scrollbar{height:6px}
.work-rail::-webkit-scrollbar-track{background:transparent}
.work-rail::-webkit-scrollbar-thumb{background:var(--rule); border-radius:99px}
.work-rail::-webkit-scrollbar-thumb:hover{background:var(--cream-faint)}

.work-tile{
  flex:0 0 auto;
  width:clamp(250px,30vw,400px);
  /* Wider than the rail's own edge padding, so a tile focused at either end
     scrolls clear rather than landing under the gutter (WCAG 2.4.11). */
  scroll-margin-inline:clamp(1.4rem,7vw,6rem);
  /* rail.js measures THIS element and transforms .tile-inner. Never put a
     transform here — measuring a transformed element feeds back on itself. */
}
.tile-inner{
  position:relative;
  isolation:isolate;               /* keeps one tile's shield off its neighbour */
  display:block;
  padding:10px;
  border-radius:16px;
  background:var(--glass-bg);
  border:1px solid var(--glass-border);
  backdrop-filter:blur(14px) saturate(1.25);
  -webkit-backdrop-filter:blur(14px) saturate(1.25);
  transform-origin:center center;
  will-change:transform, opacity;
  /* Fast + snap-eased on purpose — a tile becoming centred should read as a
     hard cut into focus, matching the site's chromatic/kinetic energy, not a
     slow cinematic crossfade. The transform itself (set per-frame by rail.js,
     following scroll 1:1) is deliberately NOT transitioned — a transition
     there would lag behind the scroll instead of tracking it. */
  transition:border-color .12s var(--ease-snap), box-shadow .16s var(--ease-snap);
}
.work-tile.is-centre .tile-inner{
  border-color:var(--accent-2);
  box-shadow:0 20px 55px -14px rgba(0,0,0,.85), 0 0 0 2px var(--accent-2), 0 0 34px -6px rgba(198,255,77,.55);
}

.stage{
  position:relative; display:block; overflow:hidden;
  width:100%; aspect-ratio:16/9;
  border-radius:9px; background:var(--ink-2);
}
.stage .shot{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover; object-position:top center;
  transition:opacity .18s var(--ease-snap);
}
/* The embed. pointer-events:none is what makes it purely a moving picture —
   without it the framed site swallows every wheel, touch and click over the
   tile. It sits UNDER the link's shield (z-index below .tile-link::after).
   Fade is quick — a hard reveal, matching the rest of the rail's snappier
   motion — rather than a slow crossfade. */
.stage .live{
  position:absolute; top:0; left:0;
  width:1600px; height:900px; border:0;
  transform-origin:0 0;            /* rail.js sets the scale factor */
  pointer-events:none;
  opacity:0; transition:opacity .2s var(--ease-snap);
  z-index:1;
}
.stage .live.on{opacity:1}
.work-tile.is-live .shot{opacity:0}

.work-meta{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:.75rem; padding:.85rem .3rem .25rem; margin:0;
}
.work-cat{
  font-family:var(--display); font-weight:700;
  font-size:clamp(.86rem,1.4vw,1rem); line-height:1.25;
  text-decoration:none; color:inherit;
}
.work-sub{
  font-family:var(--mono); font-size:.6rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--cream-faint); white-space:nowrap;
}
/* The shield: the link itself covers the whole tile, above the embed. Keeping
   it a real <a> rather than a JS click handler is what preserves middle-click,
   ctrl/cmd-click and "open in new tab". */
.tile-link::after{content:""; position:absolute; inset:0; z-index:3}
.tile-link:focus-visible{outline:3px solid var(--accent-2); outline-offset:4px}
.work-tile.is-centre .work-cat{color:var(--accent-2)}
.work-cat::after{
  content:"↗"; font-family:var(--body); margin-inline-start:.35em;
  color:var(--accent-2); opacity:0; transition:opacity .35s ease;
}
.work-tile.is-centre .work-cat::after{opacity:1}

.rail-hint{
  font-family:var(--mono); font-size:.64rem; letter-spacing:.12em;
  text-transform:uppercase; color:var(--cream-faint); margin:.4rem 0 0;
}

/* No live embeds and no drift under reduced motion (rail.js enforces the same
   thing); the centre cue becomes a static ring instead of a size change. */
@media (prefers-reduced-motion: reduce){
  .tile-inner{transition:none}
  .stage .live{display:none}
}
/* Forced-colours mode drops box-shadow entirely, so the centre tile needs an
   outline to stay distinguishable. */
@media (forced-colors: active){
  .work-tile.is-centre .tile-inner{outline:3px solid Highlight}
}
@media print{
  .stage .live{display:none}
  .work-rail{overflow:visible; flex-wrap:wrap}
}
@media (max-width:760px){
  .work-tile{width:clamp(240px,80vw,320px)}
  .work-rail{gap:1rem}
}

/* ---------- liquid-glass infinite carousel (Designed by Anchors) ----------
   A different showcase mechanism from the homepage's coverflow rail above:
   a continuously auto-scrolling row of frosted, tinted glass cards instead of
   a centre-weighted 3D stack. Track holds each item twice back-to-back so a
   translateX(-50%) loop is seamless; the second half is aria-hidden/inert to
   screen readers and keyboard nav. Driven by assets/glass-carousel.js. */
.glass-rail{
  position:relative; overflow:hidden; margin:1.2rem -1.5rem 0; padding:1.4rem 0;
  -webkit-mask-image:linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
  mask-image:linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}
.glass-track{
  display:flex; gap:1.6rem; width:max-content;
  animation:glass-scroll 46s linear infinite;
  padding:0 1.5rem;
}
.glass-track.paused,.glass-track.hover-pause{animation-play-state:paused}
@keyframes glass-scroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}

.glass-card{
  --mx:50%; --my:50%;
  position:relative; flex:0 0 auto; width:min(74vw,300px);
  border-radius:1.3rem; overflow:hidden; display:block; text-decoration:none; color:inherit;
  background:
    radial-gradient(120% 130% at 12% -10%, rgba(255,45,107,.22), transparent 55%),
    radial-gradient(120% 130% at 100% 115%, rgba(198,255,77,.16), transparent 55%),
    linear-gradient(165deg, rgba(245,241,236,.09), rgba(245,241,236,.02));
  border:1px solid rgba(245,241,236,.16);
  backdrop-filter:blur(18px) saturate(1.5);
  -webkit-backdrop-filter:blur(18px) saturate(1.5);
  box-shadow:0 20px 40px -22px rgba(0,0,0,.65), inset 0 1px 0 rgba(255,255,255,.16);
  transform:translateY(0) scale(1);
  transition:transform .35s var(--ease-snap), box-shadow .35s var(--ease-snap), border-color .35s var(--ease-snap);
}
.glass-card::after{
  content:""; position:absolute; top:0; left:10%; right:10%; height:1px; z-index:2; pointer-events:none;
  background:linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent);
}
.glass-card .glass-glow{
  position:absolute; inset:0; z-index:1; pointer-events:none; opacity:0;
  background:radial-gradient(180px circle at var(--mx) var(--my), rgba(255,255,255,.28), transparent 62%);
  transition:opacity .3s ease;
}
.glass-card .glass-shine{
  position:absolute; inset:0; z-index:2; pointer-events:none;
  background:linear-gradient(115deg, transparent 42%, rgba(255,255,255,.4) 50%, transparent 58%);
  transform:translateX(-130%);
  transition:transform .7s cubic-bezier(.2,.9,.3,1);
}
.glass-card img{
  position:relative; z-index:0; display:block; width:100%; aspect-ratio:16/9; object-fit:cover;
  filter:saturate(1.08) contrast(1.03);
}
.glass-meta{
  position:absolute; left:0; right:0; bottom:0; z-index:3;
  display:flex; align-items:baseline; gap:.7rem; padding:1.1rem 1.1rem 1rem;
  background:linear-gradient(to top, rgba(8,5,12,.88), transparent);
}
.glass-num{font-family:var(--mono); font-size:.68rem; color:var(--accent-2); font-weight:600; letter-spacing:.04em}
.glass-name{font-family:var(--display); font-weight:700; font-size:.98rem; color:var(--cream); letter-spacing:-.01em}
.glass-card:hover,.glass-card:focus-visible{
  transform:translateY(-10px) scale(1.045);
  border-color:rgba(198,255,77,.55);
  box-shadow:0 32px 60px -20px rgba(0,0,0,.75), 0 0 42px rgba(255,45,107,.28), inset 0 1px 0 rgba(255,255,255,.32);
}
.glass-card:hover .glass-glow,.glass-card:focus-visible .glass-glow{opacity:1}
.glass-card:hover .glass-shine,.glass-card:focus-visible .glass-shine{transform:translateX(130%)}
.glass-card:focus-visible{outline:2px solid var(--accent-2); outline-offset:3px}

/* products.html — one STATIC carousel per product. Same .glass-card shell as
   the homepage's portfolio rail (frosted glass, hover lift, pointer glow), but
   it does not drive itself: each product's rail is swiped/scrolled by hand and
   snaps slide to slide, so the rail has to actually scroll rather than clip,
   and there is no duplicate half and no pause toggle — there is nothing
   running to pause. Each slide is one pain paired with the benefit that kills
   it, and .tile-link stretches the slide's own .cta across the whole card so
   the entire slide is the click target without nesting a second <a>. */
.glass-rail.static{overflow-x:auto; scroll-snap-type:x mandatory; -webkit-overflow-scrolling:touch}
.glass-rail.static::-webkit-scrollbar{height:6px}
.glass-rail.static::-webkit-scrollbar-track{background:transparent}
.glass-rail.static::-webkit-scrollbar-thumb{background:var(--rule); border-radius:99px}
.glass-rail.static::-webkit-scrollbar-thumb:hover{background:var(--cream-faint)}
.glass-track.static{animation:none}
.product-slide{
  width:min(86vw,340px); padding:1.9rem 1.7rem 1.7rem;
  display:flex; flex-direction:column; scroll-snap-align:center;
}
/* .glass-card was built for a photo (z-index 0) under a caption (z-index 3),
   so its glow sits at z-index 1 — text slides have to be lifted above it the
   same way .glass-tile lifts its own children. */
.product-slide > *{position:relative; z-index:2}
.product-slide .building-badge{align-self:flex-start}
.product-slide .pain{font-size:.94rem; line-height:1.6; color:var(--cream-dim); margin:0 0 1rem}
.product-slide .gain{
  font-family:var(--display); font-weight:700; font-size:1.12rem; line-height:1.45;
  color:var(--cream); margin:0 0 1.6rem;
}
.product-slide .cta{margin-top:auto; align-self:flex-start; padding:.9rem 1.7rem; font-size:.7rem}
/* the rail scrolls its own overflow, so a slide's focus ring would be clipped
   at the edges — the card's existing hover/focus lift carries the cue instead */
.product-slide .tile-link:focus-visible{outline:2px solid var(--accent-2); outline-offset:3px}
/* Each product rail gets its own accent and a bleed numeral, so three stacked
   carousels read as three products rather than the same widget three times. */
.prod-block{--svc:var(--accent)}
.prod-ghost{
  top:-2.6rem; right:0; font-size:clamp(4.5rem,11vw,8.5rem);
  -webkit-text-stroke:1.5px var(--svc); opacity:.16;
}
html[dir="rtl"] .prod-ghost{right:auto; left:0}
.prod-name{
  font-family:var(--display); font-weight:800; letter-spacing:-.02em; line-height:1.02;
  font-size:clamp(1.5rem,3.2vw,2.3rem); margin:0; color:var(--cream); position:relative; z-index:1;
}
.prod-block .product-price{color:var(--svc)}
.prod-block .building-badge{color:var(--svc); border-color:var(--svc)}
.prod-block .building-badge .pulse{background:var(--svc)}
.product-price{
  font-family:var(--mono); font-size:.76rem; letter-spacing:.04em; color:var(--accent-2);
  margin:.6rem 0 1rem; position:relative; z-index:1;
}
/* the benefit is the line that has to land, so it gets the display face and the
   pain above it is set back — the slide reads as one beat, not two paragraphs */
.product-slide .pain{font-size:.9rem; color:var(--cream-faint)}
.product-slide .gain{font-size:1.2rem; letter-spacing:-.01em}

/* services.html — per-service offer blocks. Each block names the premium-agency
   reference price it is deliberately set against, so the multiple is the
   argument being made rather than a markup hidden in a quote. */
/* Each service block owns an accent via --svc, and everything inside it that
   can carry colour takes that one accent: the bleed number, the top rule, the
   week markers, the section labels, the guarantee border. Seven blocks that
   would otherwise read as one repeated card now read as seven things. */
.svc-block{--svc:var(--accent-2); overflow:hidden}
.svc-block::before{
  content:""; position:absolute; top:0; left:0; right:0; height:3px; z-index:3;
  background:linear-gradient(90deg, var(--svc), transparent 78%);
}
/* two classes deep on purpose: `.glass-tile > *` later in this file forces
   position:relative/z-index:1 on every direct child, which would drop this
   numeral into the flow as a 15rem block. This has to outrank it. */
.glass-tile .svc-ghost{
  position:absolute; z-index:0;
  top:-3.2rem; right:-1.5rem; font-size:clamp(7rem,17vw,15rem);
  -webkit-text-stroke:1.5px var(--svc); opacity:.15;
}
html[dir="rtl"] .glass-tile .svc-ghost{right:auto; left:-1.5rem}
.svc-name{
  font-family:var(--display); font-weight:800; letter-spacing:-.02em; line-height:1.02;
  font-size:clamp(1.6rem,3.6vw,2.7rem); margin:0 0 .8rem; color:var(--cream);
}
.svc-block .tier-name,
.svc-block .phase-wk,
.svc-block .category-label,
.svc-block .projects-heading{color:var(--svc)}
.svc-block .commit-block{border-color:var(--svc)}
.svc-block:hover,.svc-block:focus-within{border-color:var(--svc)}
.svc-block .svc-figure{font-size:clamp(1.7rem,3.4vw,2.4rem)}

/* the seven names as a ticker between the list and the qualifier — the same
   marquee the homepage runs, so the page picks up the site's pulse instead of
   ending on a wall of cards */
.svc-marquee{margin:1rem 0 0}

/* ---------- ultra-luxury service commissions (services.html) ----------
   Each of the seven is its own standing block — a "commission," not a list
   row — stacked vertically rather than one shared swipeable rail. The only
   thing that scrolls is the small stack of proof-points inside each block,
   and it scrolls on its own (the existing .glass-rail.static engine); nobody
   swipes anything here. */
.commission{
  --svc:var(--accent-2);
  position:relative; max-width:60rem; margin:0 auto 6rem; padding-top:2.2rem;
  border-top:1px solid rgba(245,241,236,.14);
}
.commission:first-of-type{margin-top:1rem}
.commission-kicker{
  font-family:var(--mono); font-size:.72rem; letter-spacing:.16em; text-transform:uppercase;
  color:var(--svc); margin:0 0 1rem;
}
.commission-name{
  font-family:var(--display); font-weight:800; letter-spacing:-.02em; line-height:1.03;
  font-size:clamp(2rem,4.4vw,3.2rem); margin:0 0 1.3rem; color:var(--cream);
}
.commission-hook{
  font-family:var(--display); font-weight:700; font-size:clamp(1.15rem,2.2vw,1.5rem);
  line-height:1.4; color:var(--cream); max-width:38rem; margin:0 0 1.6rem;
}
.commission-insight{
  font-size:1rem; line-height:1.75; color:var(--cream-dim); max-width:40rem; margin:0 0 2rem;
  padding-inline-start:1.4rem; border-inline-start:2px solid var(--svc);
}
.commission-price{
  display:inline-flex; align-items:baseline; gap:.6rem; margin:0 0 2rem;
  font-family:var(--mono); font-size:.72rem; letter-spacing:.1em; text-transform:uppercase; color:var(--cream-faint);
}
.commission-price strong{
  font-family:var(--display); font-weight:800; font-size:1.6rem; letter-spacing:-.01em;
  color:var(--svc); text-transform:none;
}
.commission .cta{margin-top:2rem}

/* the per-service proof stack — same auto-scrolling engine as the products
   carousels (no swipe, no drag, purely decorative motion that pauses on
   hover/focus), just narrower slides sized for one line of contrast each. */
.commission-rail{margin:0}
.commission-slide{
  width:min(78vw,320px); padding:1.5rem 1.5rem 1.6rem;
  display:flex; flex-direction:column; gap:.9rem;
}
.commission-slide .cpain{font-size:.86rem; line-height:1.6; color:var(--cream-faint)}
.commission-slide .cgain{
  font-family:var(--display); font-weight:700; font-size:1.05rem; line-height:1.4; color:var(--cream);
}

.svc-meta{display:grid; grid-template-columns:repeat(auto-fit,minmax(14rem,1fr)); gap:1.4rem 2.6rem; margin:1.5rem 0 .4rem}
.svc-figure{font-family:var(--display); font-weight:700; font-size:clamp(1.5rem,3vw,2.1rem); color:var(--cream); margin:.2rem 0 .8rem; line-height:1.1}
.svc-figure span{display:block; font-family:var(--mono); font-size:.7rem; font-weight:400; letter-spacing:.04em; color:var(--cream-faint); margin-top:.35rem}
.phase-wk{font-family:var(--mono); font-size:.66rem; letter-spacing:.09em; text-transform:uppercase; color:var(--accent-2); display:block; margin-bottom:.2rem}
.glass-tile .result-row{border-top-color:rgba(245,241,236,.14)}
.glass-tile .commit-block{max-width:none}

/* ---------- static glass tiles (Products, Services) ----------
   Same frosted/tinted glass recipe as .glass-card above, adapted for
   content-heavy cards that need to sit still and hold real text — price,
   bullets, a CTA — rather than a photo+caption scrolling past. Pointer-
   reactive glow tracks the cursor the same way (see the small inline
   script on each page that sets --mx/--my on pointermove). */
.glass-tiles{display:grid; grid-template-columns:repeat(auto-fit,minmax(19rem,1fr)); gap:1.6rem; max-width:76rem; margin:0 auto}
.glass-tile{
  --mx:50%; --my:50%;
  position:relative; padding:2.2rem; border-radius:1.3rem; overflow:hidden;
  background:
    radial-gradient(120% 130% at 12% -10%, rgba(255,45,107,.22), transparent 55%),
    radial-gradient(120% 130% at 100% 115%, rgba(198,255,77,.16), transparent 55%),
    linear-gradient(165deg, rgba(245,241,236,.09), rgba(245,241,236,.02));
  border:1px solid rgba(245,241,236,.16);
  backdrop-filter:blur(18px) saturate(1.5);
  -webkit-backdrop-filter:blur(18px) saturate(1.5);
  box-shadow:0 20px 40px -22px rgba(0,0,0,.65), inset 0 1px 0 rgba(255,255,255,.16);
  transition:transform .35s var(--ease-snap), box-shadow .35s var(--ease-snap), border-color .35s var(--ease-snap);
}
.glass-tile.hi{border-color:rgba(255,45,107,.5)}
.glass-tile::after{
  content:""; position:absolute; top:0; left:10%; right:10%; height:1px; z-index:2; pointer-events:none;
  background:linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent);
}
.glass-tile .glass-glow{
  position:absolute; inset:0; z-index:0; pointer-events:none; opacity:0;
  background:radial-gradient(220px circle at var(--mx) var(--my), rgba(255,255,255,.16), transparent 62%);
  transition:opacity .3s ease;
}
.glass-tile:hover .glass-glow,.glass-tile:focus-within .glass-glow{opacity:1}
.glass-tile:hover,.glass-tile:focus-within{
  transform:translateY(-6px);
  border-color:rgba(198,255,77,.55);
  box-shadow:0 28px 50px -20px rgba(0,0,0,.7), 0 0 34px rgba(255,45,107,.22), inset 0 1px 0 rgba(255,255,255,.28);
}
.glass-tile > *{position:relative; z-index:1}
.glass-tile ul{list-style:none; margin:1.1rem 0; padding:0}
.glass-tile li{font-size:.9rem; color:var(--cream-dim); padding:.55rem 0; border-top:1px solid rgba(245,241,236,.14)}
.glass-tile li:first-child{border-top:none}
/* .tier-name / .price were only ever styled under .price-card, but the tiles on
   products.html use the same two classes — so the label rendered as plain body
   copy and the qualifier span inherited the headline size. Same treatment as the
   price cards, scoped here so both card families stay in step. */
.glass-tile .tier-name{font-family:var(--mono); font-size:.72rem; letter-spacing:.1em; text-transform:uppercase; color:var(--accent-2); margin:0 0 .8rem}
.glass-tile .price{font-family:var(--display); font-weight:700; margin:0 0 .3rem; color:var(--cream)}
.glass-tile .price span{font-family:var(--mono); font-size:.9rem; font-weight:400; color:var(--cream-faint); letter-spacing:0; text-transform:none}

.category-block{margin-top:1.3rem; padding-top:1.1rem; border-top:1px solid rgba(245,241,236,.14)}
.category-label{font-family:var(--mono); font-size:.66rem; letter-spacing:.1em; text-transform:uppercase; color:var(--accent-2); margin:0 0 .35rem}
.category-item{font-size:.88rem; color:var(--cream-dim); margin:0}
.category-block.is-empty .category-label{color:var(--cream-faint)}
.category-block.is-empty .category-item{color:var(--cream-faint); font-style:italic}
.category-block.is-empty .category-item a{color:var(--accent-3); font-style:normal}

@media (prefers-reduced-motion:reduce){
  .glass-track{animation:none}
  .glass-rail{overflow-x:auto; -webkit-overflow-scrolling:touch}
  .glass-card.dup{display:none}
  .glass-card,.glass-card .glass-shine{transition:none}
}
@media (forced-colors:active){
  .glass-card:hover,.glass-card:focus-visible{outline:3px solid Highlight}
}
@media print{
  .glass-rail{overflow-x:visible; mask-image:none}
  .glass-track{animation:none; flex-wrap:wrap}
  .glass-card.dup{display:none}
}
@media (max-width:760px){
  .glass-card{width:clamp(220px,78vw,290px)}
  .glass-rail{margin-inline:-1rem}
}

/* ---------- WebGL shader glass carousel (Designed by Anchors) ----------
   The real-time refraction/chromatic-aberration version of the carousel
   above — assets/glass-shader-carousel.js renders the distorted photo into
   #glass-gl and swaps this in over the CSS version when WebGL is available.
   Text lives in real DOM nodes overlaid on the canvas (glass-gl-labels) so
   titles/CTAs stay crisp while only the photo behind them warps. */
#glass-gl{
  display:none; width:100%; height:24rem; margin:1.2rem 0 0; border-radius:1.3rem;
}
.glass-gl-wrap{position:relative}
.glass-gl-labels{
  position:absolute; top:0; left:0; pointer-events:none;
}
.glass-gl-tile{
  position:absolute; top:50%; left:0; pointer-events:none;
  padding:0 1.5rem; opacity:0; transition:opacity .25s ease, transform .2s var(--ease-snap);
  text-align:left; display:flex; flex-direction:column; gap:.5rem;
}
.glass-gl-tag{
  font-family:var(--mono); font-size:.6rem; letter-spacing:.12em; text-transform:uppercase;
  color:var(--accent-2); margin:0; line-height:1;
  text-shadow:0 1px 6px rgba(0,0,0,.8);
}
.glass-gl-title{
  font-family:var(--display); font-weight:700; font-size:1.2rem; color:var(--cream);
  margin:0; letter-spacing:-.01em; line-height:1.15;
  text-shadow:0 2px 10px rgba(0,0,0,.85);
}
.glass-gl-desc{
  font-size:.8rem; color:var(--cream-dim); margin:0; line-height:1.4;
  max-width:14rem; text-shadow:0 1px 8px rgba(0,0,0,.9);
}
.glass-gl-enter{
  display:inline-block; align-self:flex-start; font-family:var(--mono); font-size:.66rem; letter-spacing:.08em;
  text-transform:uppercase; color:var(--ink); background:var(--accent-2);
  padding:.4rem .9rem; border-radius:99px; margin-top:.2rem;
  transition:background .2s ease, transform .2s var(--ease-snap), box-shadow .2s ease;
}
/* .is-hover is toggled per-frame by glass-shader-carousel.js to match
   whichever tile the pointer is actually over — the shader's own bulge/CA
   response lives in the pixels, this is the DOM half of the same signal so
   the hover feedback reads clearly instead of hiding entirely inside a
   subtle lens-distortion effect. */
.glass-gl-tile.is-hover .glass-gl-enter{
  background:var(--cream); transform:scale(1.08);
  box-shadow:0 6px 18px -6px rgba(198,255,77,.6);
}
@media (max-width:760px){
  #glass-gl{height:20rem}
  .glass-gl-desc{display:none}
}

/* ---------- floating WhatsApp button ----------
   Recoloured to the brand accents rather than WhatsApp's own green, so it
   reads as an Anchors element that happens to open WhatsApp, not a
   third-party widget bolted onto the page. Sits below the mobile full-screen
   nav overlay (z-index 9) so opening the menu doesn't leave it floating on top. */
.wa-float{
  position:fixed; bottom:1.7rem; right:1.7rem; z-index:6;
  width:3.4rem; height:3.4rem; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:rgba(11,7,16,.55); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
  border:1.5px solid var(--accent-2);
  color:var(--accent-2);
  box-shadow:0 10px 30px -8px rgba(0,0,0,.6), inset 0 0 0 1px rgba(198,255,77,.12);
  transition:transform .14s var(--ease-snap), box-shadow .2s var(--ease-snap), border-color .2s var(--ease-snap), color .2s var(--ease-snap), background .2s var(--ease-snap);
}
.wa-float svg{
  width:1.5rem; height:1.5rem; display:block;
  fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round;
}
.wa-float::before{
  content:''; position:absolute; inset:-1.5px; border-radius:50%;
  border:1.5px solid var(--accent-2); opacity:.55; pointer-events:none;
  animation:wa-pulse 2.4s var(--ease-snap) infinite;
}
@keyframes wa-pulse{
  0%{transform:scale(1); opacity:.5}
  70%{transform:scale(1.5); opacity:0}
  100%{transform:scale(1.5); opacity:0}
}
.wa-float:hover,.wa-float:focus-visible{
  transform:scale(1.1) translateY(-2px) rotate(-3deg);
  border-color:var(--accent); color:var(--accent);
  background:rgba(255,45,107,.1);
  box-shadow:0 14px 38px -6px rgba(0,0,0,.7), 0 0 30px rgba(255,45,107,.5), inset 0 0 0 1px rgba(255,45,107,.25);
}
@media (prefers-reduced-motion:reduce){.wa-float::before{animation:none}}
/* Mirrors to the opposite corner in RTL, matching how the rest of the nav flips. */
html[dir="rtl"] .wa-float{right:auto; left:1.7rem}
@media (max-width:760px){
  .wa-float{width:3.1rem; height:3.1rem; bottom:1.2rem; right:1.2rem}
  html[dir="rtl"] .wa-float{right:auto; left:1.2rem}
}
@media print{.wa-float{display:none}}

/* ---------- floating Site Grader button ----------
   Same recipe as .wa-float (frosted circle, pulse ring, brand accents) since
   the two are meant to read as siblings, just stacked above it so neither
   corner gets crowded. Site Grader lost its own top-nav slot when it merged
   into Tools; this is now the only nav-level way to reach it from anywhere. */
.gift-float{
  position:fixed; bottom:5.7rem; right:1.7rem; z-index:6;
  width:3.4rem; height:3.4rem; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:rgba(11,7,16,.55); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
  border:1.5px solid var(--accent-2);
  color:var(--accent-2);
  box-shadow:0 10px 30px -8px rgba(0,0,0,.6), inset 0 0 0 1px rgba(198,255,77,.12);
  transition:transform .14s var(--ease-snap), box-shadow .2s var(--ease-snap), border-color .2s var(--ease-snap), color .2s var(--ease-snap), background .2s var(--ease-snap);
}
.gift-float svg{
  width:1.5rem; height:1.5rem; display:block;
  fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round;
}
.gift-float::before{
  content:''; position:absolute; inset:-1.5px; border-radius:50%;
  border:1.5px solid var(--accent-2); opacity:.55; pointer-events:none;
  animation:wa-pulse 2.4s var(--ease-snap) infinite; animation-delay:1.2s;
}
.gift-float:hover,.gift-float:focus-visible{
  transform:scale(1.1) translateY(-2px) rotate(3deg);
  border-color:var(--accent); color:var(--accent);
  background:rgba(255,45,107,.1);
  box-shadow:0 14px 38px -6px rgba(0,0,0,.7), 0 0 30px rgba(255,45,107,.5), inset 0 0 0 1px rgba(255,45,107,.25);
}
@media (prefers-reduced-motion:reduce){.gift-float::before{animation:none}}
html[dir="rtl"] .gift-float{right:auto; left:1.7rem}
@media (max-width:760px){
  .gift-float{width:3.1rem; height:3.1rem; bottom:4.7rem; right:1.2rem}
  html[dir="rtl"] .gift-float{right:auto; left:1.2rem}
}
@media print{.gift-float{display:none}}

/* ---------- cookie consent banner ----------
   Only ever appears once assets/consent.js has a real Meta Pixel ID
   configured — dormant otherwise. Equal-weight accept/reject buttons on
   purpose: a bigger "Accept" and a buried "Decline" is the dark pattern
   GDPR guidance specifically calls out. */
.consent-banner{
  position:fixed; left:0; right:0; bottom:0; z-index:50;
  display:flex; flex-wrap:wrap; align-items:center; justify-content:center; gap:1.2rem 2rem;
  padding:1.2rem 1.6rem; background:rgba(11,7,16,.92);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  border-top:1.5px solid var(--rule);
  transform:translateY(100%); transition:transform .3s var(--ease-snap);
}
.consent-banner.is-visible{transform:translateY(0)}
.consent-banner p{
  margin:0; max-width:44rem; font-size:.88rem; line-height:1.6; color:var(--cream-dim);
}
.consent-banner p a{color:var(--accent-2)}
.consent-actions{display:flex; gap:.8rem; flex-shrink:0}
.consent-actions .cta{padding:.7rem 1.4rem; font-size:.85rem; border:none; cursor:pointer}
.consent-actions .cta.ghost{border:2px solid var(--rule); background:transparent; color:var(--cream-dim)}
/* dismiss X — deliberately unstyled as a "choice": no border, no fill, just
   an icon in the corner. Closing it (like ignoring the banner outright) reads
   as essential-only, the same outcome as the Essential Only button, just
   without asking the visitor to make a labeled decision. */
.consent-close{
  position:absolute; top:.7rem; inset-inline-end:.9rem;
  width:1.8rem; height:1.8rem; display:flex; align-items:center; justify-content:center;
  background:none; border:none; color:var(--cream-faint); cursor:pointer; font-size:1.1rem; line-height:1;
  padding:0; border-radius:99px; transition:color .2s ease;
}
.consent-close:hover,.consent-close:focus-visible{color:var(--cream)}
@media (max-width:600px){
  .consent-banner{justify-content:flex-start; padding:1.2rem 2.6rem 1.2rem 1.2rem}
  .consent-actions{width:100%}
  .consent-actions .cta{flex:1}
}
@media print{.consent-banner{display:none}}
