/* ============================================================
   Marketing homepage — globals
   Loaded by public/index.html and public/sandbox-login.html.
   Section-specific styles live in src/styles/sections/*.css.
   The sandbox app uses src/styles/sandbox-app.css.
============================================================ */

/* Self-hosted variable fonts. Geist + Manrope ship as single variable .woff2
   files that cover the 100–900 weight range, so one @font-face per family
   covers every weight we use (400/500/600/700). */
@font-face{
  font-family:"Geist";
  font-style:normal;
  font-weight:100 900;
  font-display:swap;
  src:url("/assets/fonts/geist-variable.woff2") format("woff2-variations"),
      url("/assets/fonts/geist-variable.woff2") format("woff2");
}
@font-face{
  font-family:"Manrope";
  font-style:normal;
  font-weight:200 800;
  font-display:swap;
  src:url("/assets/fonts/manrope-variable.woff2") format("woff2-variations"),
      url("/assets/fonts/manrope-variable.woff2") format("woff2");
}
:root{
  --bg:#fafaf7;
  --bg-soft:#f3f3ee;
  --bg-card:#ffffff;
  --paper:#fafaf7;
  --line:rgba(15,15,20,0.08);
  --line-strong:rgba(15,15,20,0.14);
  --ink:#0e0e12;
  --ink-dim:#5a5a64;
  /* 5.0:1 on --bg, 5.3:1 on white: AA for the small text that uses it. */
  --ink-mute:#6b6b74;

  --purple:#8F53A1;
  --purple-deep:#6f3d80;
  --turquoise:#24A2B1;
  --turquoise-deep:#1d7e8a;
  --orange:#e1850a;
  --orange-deep:#d17a04;
  --green:#8cae1c;
  --green-deep:#80a100;
  --red:#D51F2F;

  /* Button surface + its label colour. All four track the base accent, so a
     brand button always matches the accent used elsewhere on the page. The
     seam stays so a button can be retuned without moving the brand token. */
  --purple-btn:var(--purple);       --purple-btn-fg:#ffffff;
  --turquoise-btn:var(--turquoise); --turquoise-btn-fg:#ffffff;
  --orange-btn:var(--orange);       --orange-btn-fg:#ffffff;
  --green-btn:var(--green);         --green-btn-fg:#ffffff;

  /* Active capability-pill label. White here is deliberate, accepted below AA
     (2.57–3.05:1) on the light fills. Don't "fix" without asking. */
  --pill-fg:#ffffff;

  --font-display:"Geist",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --font-body:"Manrope",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;

  --maxw:1240px;
  --radius-lg:22px;
  --radius-md:14px;
  --radius-sm:10px;

  --surface-elevated:#ffffff;
  --card-inset:rgba(255,255,255,0.6);
  --fill-subtle:rgba(15,15,20,0.05);
  --fill-subtle-strong:rgba(15,15,20,0.08);
  --grid-line:rgba(15,15,20,0.04);
}

/* ---- Dark theme ---- */
:root[data-theme="dark"]{
  --bg:#252527; --bg-soft:#272729; --bg-card:#2a2a2c; --paper:#252527;
  --line:rgba(255,255,255,0.10); --line-strong:rgba(255,255,255,0.16);
  --ink:#f5f5f7; --ink-dim:#cccccc; --ink-mute:#9a9aa0;
  --purple:#b06fc4; --purple-deep:#cf8ce4;
  --turquoise:#3cc0d0; --turquoise-deep:#53b2c0;
  --orange:#e1850a; --orange-deep:#ec8f21;
  --green:#8cae1c; --green-deep:#8eb121;
  --red:#ef4452;
  --purple-btn:var(--purple);       --purple-btn-fg:#ffffff;
  --turquoise-btn:var(--turquoise); --turquoise-btn-fg:#ffffff;
  --orange-btn:var(--orange);       --orange-btn-fg:#ffffff;
  --green-btn:var(--green);         --green-btn-fg:#ffffff;
  /* Dark accents are bright enough that a near-black label reads on all four. */
  --pill-fg:#0e0e12;
  --surface-elevated:#333335;
  --card-inset:rgba(255,255,255,0.05);
  --fill-subtle:rgba(255,255,255,0.05);
  --fill-subtle-strong:rgba(255,255,255,0.10);
  --grid-line:rgba(255,255,255,0.05);
}

*{box-sizing:border-box;margin:0;padding:0;}
html{scroll-behavior:smooth;touch-action:pan-y pinch-zoom;}
/* `overflow-x: clip` blocks horizontal scroll without creating a scroll
   container (unlike `hidden`), so position:sticky on .site-header keeps
   working. `touch-action: pan-y pinch-zoom` above blocks horizontal swipe
   but explicitly preserves pinch-to-zoom — critical for low-vision users
   on a page about accessibility. */
body{
  font-family:var(--font-body);background:var(--bg);color:var(--ink);
  font-size:16px;line-height:1.55;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  overflow-x:clip;
  /* containing block for the page wash below */
  position:relative;
}
img{max-width:100%;display:block;}
a{color:inherit;text-decoration:none;}
button{font:inherit;background:none;border:0;color:inherit;cursor:pointer;}
::selection{background:var(--purple);color:#fff;}
:root[data-theme="dark"] ::selection{background:var(--ink);color:var(--bg);}

h1,h2,h3,h4,h5,h6{font-family:var(--font-display);font-weight:500;letter-spacing:-0.02em;}

.skip-link{
  position:absolute;left:-9999px;top:0;
  padding:10px 14px;background:var(--ink);color:var(--bg);border-radius:8px;
  font-size:14px;z-index:200;
}
.skip-link:focus{left:14px;top:14px;}

.visually-hidden{
  position:absolute !important;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}

/* The Tabler "loader-2" glyph is a static circle with a gap — give it a
   continuous rotation wherever it appears so it actually spins. */
@keyframes ti-spin{
  from { transform:rotate(0deg); }
  to   { transform:rotate(360deg); }
}
i.ti-loader-2{ display:inline-block; animation:ti-spin 0.8s linear infinite; }
@media (prefers-reduced-motion: reduce){
  i.ti-loader-2{ animation:none; }
}

/* ============================================================
   PAGE WASH — the hero's palette, carried down the page
   The hero owns a dense orb mesh (sections/hero.css); this is the same
   palette continued underneath everything below it, so the page doesn't
   fall off a cliff into bare paper once the hero scrolls away.

   Absolute rather than fixed, with body as the containing block, so
   `inset:0` spans the whole document instead of one viewport. Fixed
   would pin the same colours to the same screen position at every
   scroll depth, which reads as a vignette on the window — the point
   here is colour that belongs to the page and travels with it.

   Orbs alternate sides going down and are enormous, because calm comes
   from few large forms, not from low alpha. Peak contrast (alpha x span
   on the dominant channel) is 12-15 levels, against roughly 60 in the
   hero itself, so the hero stays the loudest thing on the page.

     purple    light  .090 x (250-83)  = 15 levels  [green]
     turquoise light  .070 x (250-36)  = 15 levels  [red]
     orange    light  .060 x (247-10)  = 14 levels  [blue]
     turquoise light  .055 x (250-36)  = 12 levels  [red]

   Nothing starts before 12% of page height, so the hero's own mesh isn't
   muddied by a second layer underneath it. Every ramp ends on
   rgba(colour,0) rather than `transparent`, which is transparent BLACK
   and would drag grey through each fade.
============================================================ */
body::before{
  content:"";
  position:absolute;
  inset:0;
  z-index:0;
  pointer-events:none;
  background:
    radial-gradient(900px 700px at 88% 12%,
      rgba(36,162,177,.070) 0%,  rgba(36,162,177,.052) 34%,
      rgba(36,162,177,.028) 62%, rgba(36,162,177,.009) 84%,
      rgba(36,162,177,0)    100%),
    radial-gradient(880px 720px at 6% 30%,
      rgba(143,83,161,.090) 0%,  rgba(143,83,161,.067) 34%,
      rgba(143,83,161,.036) 62%, rgba(143,83,161,.012) 84%,
      rgba(143,83,161,0)    100%),
    radial-gradient(820px 660px at 94% 48%,
      rgba(225,133,10,.060) 0%,  rgba(225,133,10,.044) 34%,
      rgba(225,133,10,.024) 62%, rgba(225,133,10,.008) 84%,
      rgba(225,133,10,0)    100%),
    radial-gradient(860px 700px at 12% 66%,
      rgba(36,162,177,.055) 0%,  rgba(36,162,177,.041) 34%,
      rgba(36,162,177,.022) 62%, rgba(36,162,177,.007) 84%,
      rgba(36,162,177,0)    100%),
    radial-gradient(900px 720px at 82% 84%,
      rgba(143,83,161,.070) 0%,  rgba(143,83,161,.052) 34%,
      rgba(143,83,161,.028) 62%, rgba(143,83,161,.009) 84%,
      rgba(143,83,161,0)    100%);
}

/* Leather grain over the whole page. Besides the texture, it dithers the
   gradient washes, which are shallow enough to band without grain. The SVG
   carries its own see-through shadows and highlights, so it suits both themes. */
body::after{
  content:"";
  position:absolute;
  inset:0;
  z-index:0;
  pointer-events:none;
  background-image:url("/assets/leather-texture.svg");
}

:root[data-theme="dark"] body::before{
  background:
    radial-gradient(900px 700px at 88% 12%,
      rgba(60,192,208,.090) 0%,  rgba(60,192,208,.067) 34%,
      rgba(60,192,208,.036) 62%, rgba(60,192,208,.012) 84%,
      rgba(60,192,208,0)    100%),
    radial-gradient(880px 720px at 6% 30%,
      rgba(176,111,196,.110) 0%, rgba(176,111,196,.081) 34%,
      rgba(176,111,196,.044) 62%, rgba(176,111,196,.014) 84%,
      rgba(176,111,196,0)    100%),
    radial-gradient(820px 660px at 94% 48%,
      rgba(240,170,70,.070) 0%,  rgba(240,170,70,.052) 34%,
      rgba(240,170,70,.028) 62%, rgba(240,170,70,.009) 84%,
      rgba(240,170,70,0)    100%),
    radial-gradient(860px 700px at 12% 66%,
      rgba(60,192,208,.070) 0%,  rgba(60,192,208,.052) 34%,
      rgba(60,192,208,.028) 62%, rgba(60,192,208,.009) 84%,
      rgba(60,192,208,0)    100%),
    radial-gradient(900px 720px at 82% 84%,
      rgba(176,111,196,.090) 0%, rgba(176,111,196,.067) 34%,
      rgba(176,111,196,.036) 62%, rgba(176,111,196,.012) 84%,
      rgba(176,111,196,0)    100%);
}

main, header, footer{position:relative;z-index:1;}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 28px;}

/* ============================================================
   HEADER
============================================================ */
.site-header{
  position:sticky;top:0;z-index:50;
  padding:14px 28px;
  transition:padding .4s cubic-bezier(.2,.8,.2,1);
}
.site-header.scrolled{padding:18px 28px;}

.header-inner{
  position:relative;
  max-width:calc(var(--maxw) - 56px);margin:0 auto;
  display:flex;align-items:center;justify-content:space-between;gap:24px;
  background:transparent;
  border:1px solid transparent;
  border-radius:999px;
  padding:8px 8px 8px 18px;
  box-shadow:none;
  backdrop-filter:none;-webkit-backdrop-filter:none;
  transition:background .4s cubic-bezier(.2,.8,.2,1),
             backdrop-filter .4s cubic-bezier(.2,.8,.2,1),
             border-color .4s cubic-bezier(.2,.8,.2,1),
             max-width .4s cubic-bezier(.2,.8,.2,1),
             box-shadow .4s cubic-bezier(.2,.8,.2,1);
}
.site-header.scrolled .header-inner{
  max-width:980px;
  background:color-mix(in srgb, var(--bg-card) 82%, transparent);
  backdrop-filter:blur(14px) saturate(140%);-webkit-backdrop-filter:blur(14px) saturate(140%);
  border-color:var(--line);
  box-shadow:0 10px 28px rgba(15,15,20,0.06);
}

.brand{display:flex;align-items:center;gap:12px;}
.brand-mark{width:46px;height:46px;display:grid;place-items:center;}
.brand-mark img{
  width:46px;height:46px;
  /* Springy entrance on page load. fill-mode: backwards keeps the 0% state
     applied before the animation runs (no flash), but releases after it ends
     so :hover styles aren't overridden. */
  animation:logo-entrance 1s cubic-bezier(.34,1.56,.64,1) backwards;
  /* No rotate transition at rest — so on hover-out the rotation snaps back
     instantly (invisible, since 360° looks the same as 0°). */
  transition:filter .35s ease, scale .35s ease;
}
@keyframes logo-entrance{
  0%  {scale:0.85; opacity:0;}
  100%{scale:1; opacity:1;}
}
.brand-mark:hover img{
  scale:1.12;
  filter:drop-shadow(0 6px 14px rgba(143,83,161,0.35));
}
.brand-text .brand-name{
  font-family:var(--font-display);
  font-size:18px;font-weight:600;letter-spacing:-0.015em;line-height:1.1;white-space:nowrap;
  color:var(--ink);
  display:block;
}
.brand-text .brand-tagline{ display:block;margin-top:1px; line-height:1.2; font-family:var(--font-display); font-size:12.5px; font-weight:500; letter-spacing:0; color:var(--ink-dim); }
.nav{display:flex;gap:4px;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);}
.nav a{
  padding:8px 14px;border-radius:999px;font-size:14px;font-weight:600;color:var(--ink-dim);
  transition:color .25s ease, background .25s ease;
}
.nav a:hover{color:var(--ink);background:var(--fill-subtle);}
.header-cta{display:flex;gap:8px;align-items:center;}

/* ============================================================
   BUTTONS
============================================================ */
.btn{
  display:inline-flex;align-items:center;gap:8px;
  padding:11px 18px;border-radius:999px;
  font-family:var(--font-body);
  font-size:14px;font-weight:500;letter-spacing:-0.005em;
  transition:transform .25s ease, background .25s ease, color .25s ease, border-color .25s ease, box-shadow .25s ease, filter .25s ease;
  white-space:nowrap;border:1px solid transparent;cursor:pointer;
}
.btn i{font-size:16px;transition:transform .3s ease;}
.btn:hover i.ti-arrow-right{transform:translateX(3px);}
.btn:hover i.ti-arrow-up-right{transform:translate(3px,-3px);}

.btn-primary{
  background:var(--ink);color:var(--bg);
  box-shadow:0 1px 0 rgba(255,255,255,0.18) inset;
}
.btn-primary:hover{filter:brightness(1.1);transform:translateY(-1px);}

.btn-ghost{
  background:var(--fill-subtle);color:var(--ink);border-color:var(--line);
}
.btn-ghost:hover{background:var(--fill-subtle-strong);border-color:var(--line-strong);}

.btn-purple{
  background:var(--purple-btn);color:var(--purple-btn-fg);
  box-shadow:0 0 0 1px rgba(255,255,255,0.12) inset;
}
.btn-purple:hover{filter:brightness(1.06);transform:translateY(-1px);}

/* ============================================================
   SECTION SHELL + EYEBROW
============================================================ */
section.block{padding:96px 0 120px;position:relative;}

/* ---- Chapter opener ----
   Every main section opens the same way: a hairline rule across the
   content column, a 44x5 ink tick sitting on it, then the heading 56px
   below. Repeating it turns the page into a sequence of chapters rather
   than a stack of unrelated blocks.

   The tick is drawn on .wrap rather than the section so it lines up with
   the content column instead of the viewport. #features mirrors the
   whole opener to the right margin (see sections/features.css), so the
   three openers alternate left-right-left down the page. */
section.block > .wrap{
  position:relative;
  border-top:1px solid var(--line-strong);
  padding-top:56px;
}
section.block > .wrap::after{
  content:"";
  position:absolute;top:-3px;left:28px;
  width:44px;height:5px;
  background:var(--ink);
}

.section-head{margin-bottom:56px;max-width:760px;}
/* `.center` survives as a markup hook only. Under the chapter openers
   the heads are set to the margin, not centred — a centred head under a
   left-anchored tick reads as a mistake. */
.section-head.center{margin-left:0;margin-right:auto;text-align:left;max-width:820px;}
.section-head .eyebrow{margin-bottom:18px;}
.section-head h2{font-size:clamp(36px, 5vw, 60px);letter-spacing:-0.03em;line-height:1.02;font-weight:500;}
.section-head h2 .accent{font-weight:500;color:var(--ink-dim);}
.section-head h2 .gradient{
  font-weight:500;
  background:linear-gradient(110deg, var(--purple), var(--orange) 60%, var(--turquoise));
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.section-head p{margin-top:18px;color:var(--ink-dim);font-size:18px;max-width:580px;}
.section-head.center p{margin-left:0;margin-right:0;}

.eyebrow{ display:inline-flex;align-items:center;gap:10px;
  padding:6px 14px 6px 8px;border-radius:999px;
  background:color-mix(in srgb, var(--bg-card) 72%, transparent);border:1px solid var(--line);
  color:var(--ink-dim);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px); font-family:var(--font-display); font-size:13px; font-weight:600; letter-spacing:0; }
.eyebrow .dot{
  width:6px;height:6px;border-radius:50%;background:var(--green);
  box-shadow:0 0 0 4px rgba(140,174,28,0.22);
  animation:pulseDot 2.4s ease-in-out infinite;
}
@keyframes pulseDot{0%,100%{box-shadow:0 0 0 4px rgba(140,174,28,0.22);} 50%{box-shadow:0 0 0 8px rgba(140,174,28,0.06);}}

/* ============================================================
   REVEAL ANIMATIONS + FOCUS
============================================================ */
[data-reveal]{opacity:0;transform:translateY(18px);transition:opacity .8s ease, transform .8s ease;}
[data-reveal].in{opacity:1;transform:none;}

/* If JS fails to run (network error, ES module parse failure on an old
   browser, ad-blocker eating /script.js), script.js leaves `.no-js` on
   <html>. Reveal everything so content stays readable — same pattern used
   for prefers-reduced-motion below, and matches .title-word in hero.css. */
.no-js [data-reveal]{opacity:1 !important;transform:none !important;}
.no-js .title-word{opacity:1 !important;transform:none !important;filter:none !important;}

a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible{
  outline:2px solid var(--purple);outline-offset:2px;border-radius:6px;
}

/* ============================================================
   MOBILE NAV (hamburger button + slide-down drawer)
   Toggle button is hidden at desktop; shown at <=760px. The drawer is
   position:fixed below the header (uses --header-h written by
   headerScroll.js) and slides in via translateY. Backdrop is a separate
   element so it can fade independently and capture taps to close.
============================================================ */
.nav-toggle{
  display:none;align-items:center;justify-content:center;
  width:42px;height:42px;border-radius:50%;
  background:transparent;border:1px solid transparent;color:var(--ink);
  transition:background .2s ease, border-color .2s ease;
  /* Sit above the drawer (z-index 55) so the X close icon stays tappable
     even when the drawer is open and covering the rest of the header. */
  position:relative;z-index:60;
}
.nav-toggle:hover{background:var(--fill-subtle);}
.nav-toggle i{font-size:20px;line-height:1;}
.site-header.scrolled .nav-toggle{border-color:var(--line);}

/* Side drawer — slides in from the right at <=760px, covering the header
   too. The X close toggle gets a higher z-index so it remains tappable
   above the drawer. Completely hidden at desktop via display:none. */
.mobile-menu{
  display:none;
  position:fixed;top:0;right:0;
  width:min(320px, 85vw);
  height:100dvh;
  z-index:55;
  overflow-y:auto;
  background:color-mix(in srgb, var(--bg-card) 97%, transparent);
  backdrop-filter:blur(16px) saturate(140%);
  -webkit-backdrop-filter:blur(16px) saturate(140%);
  border-left:1px solid var(--line);
  box-shadow:-18px 0 36px rgba(15,15,20,0.10);
  transform:translateX(100%);
  transition:transform .3s cubic-bezier(.2,.8,.2,1);
  pointer-events:none;
}
.mobile-menu.is-open{transform:translateX(0);pointer-events:auto;}

/* Top-left close button inside the drawer. Positioned absolutely so the
   list content below isn't affected by its size. */
.mobile-menu__close{
  position:absolute;top:14px;left:16px;
  width:40px;height:40px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:transparent;color:var(--ink);
  border:1px solid var(--line);
  transition:background .2s ease;
}
.mobile-menu__close:hover{background:var(--fill-subtle);}
.mobile-menu__close i{font-size:18px;line-height:1;}

.mobile-menu__list{
  display:flex;flex-direction:column;gap:6px;
  /* Top padding clears the close button (14px top + 40px button + 16px gap). */
  padding:70px 20px 32px;
}
.mobile-menu__list > a:not(.btn){
  font-family:var(--font-body);font-size:17px;font-weight:500;
  color:var(--ink);padding:14px 12px;border-radius:10px;
  display:flex;align-items:center;
}
.mobile-menu__list > a:not(.btn):hover{background:var(--fill-subtle);}
.mobile-menu__list > a.btn{
  /* Buttons are left-aligned in the drawer column and the same fixed
     width so they stack as a tidy pair. `justify-content: center` keeps
     the icon + label centered INSIDE each button. */
  align-self:flex-start;width:200px;
  justify-content:center;
  padding:11px 18px;font-size:14px;margin-top:4px;
}
.mobile-menu__sep{
  border:0;border-top:1px solid var(--line);margin:10px 4px;
}

/* Hide the header hamburger when the drawer is open — the close X inside
   the drawer is the single close affordance, avoiding double-X confusion. */
body.menu-open .nav-toggle{visibility:hidden;}

.mobile-menu-backdrop{
  display:none;
  /* Full viewport — dim the entire page including under the header. The
     close toggle is raised via z-index so it stays interactive. */
  position:fixed;inset:0;
  z-index:54;
  background:rgba(15,15,20,0.4);
  opacity:0;transition:opacity .3s ease;
  pointer-events:none;
}
.mobile-menu-backdrop.is-open{opacity:1;pointer-events:auto;}

/* Lock body scroll behind the open drawer. */
body.menu-open{overflow:hidden;}

/* ============================================================
   BACK-TO-TOP BUTTON
============================================================ */
.back-to-top{
  position:fixed;right:24px;bottom:24px;z-index:60;
  width:44px;height:44px;border-radius:50%;
  display:grid;place-items:center;
  background:var(--bg-card);color:var(--ink);
  box-shadow:0 10px 24px rgba(15,15,20,0.12), 0 0 0 1px var(--line) inset;
  opacity:0;visibility:hidden;transform:translateY(8px);
  transition:opacity .3s ease, transform .3s ease, visibility .3s, filter .25s ease;
  cursor:pointer;
}
.back-to-top i{font-size:20px;line-height:1;}
.back-to-top.visible{opacity:1;visibility:visible;transform:none;}
.back-to-top:hover{filter:brightness(1.1);transform:translateY(-2px);}

/* ============================================================
   GLOBAL RESPONSIVE (header / nav / back-to-top)
============================================================ */
@media (max-width: 760px){
  .nav,.brand-text{display:none;}
  .header-cta .btn-ghost{display:none;}
  .site-header{padding:12px 16px;}
  .site-header.scrolled{padding:14px 16px;}
  section.block{padding:64px 0 80px;}
  section.block > .wrap{padding-top:40px;}
  .back-to-top{right:16px;bottom:16px;width:40px;height:40px;}
  .back-to-top i{font-size:18px;}
}

@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:0.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:0.01ms !important;
  }
  .brand-mark img{animation:none;}
  .brand-mark:hover img{rotate:0; scale:1.08;}
  .title-word{opacity:1 !important;transform:none !important;filter:none !important;}
  [data-reveal]{opacity:1 !important;transform:none !important;}
}
