/* substrate.energy splash — all page styling. See README.md.
   Extracted from index.html so the page is markup and the behaviour/styling
   live in purpose-named modules. There is no duplicate copy of any of this. */
/* ---------------------------------------------------------------------------
   TOKENS — lifted verbatim from website/index.html. Not re-picked, not re-tuned.
   The singularity mark's own gradient stops are #a855f7 / #7c3aed / #06b6d4 on
   #050510, which are these same four values, so mark and page agree by
   construction rather than by eye.
   --------------------------------------------------------------------------- */
:root{
  --bar-login-size:13.5px;   /* brand type is derived from this; see .bar-brand */
  --bg-void:#050510; --bg-deep:#0a0a1a; --bg-charcoal:#0f0f24; --bg-slate:#16163a;
  --accent-violet:#7c3aed; --accent-purple:#a855f7; --accent-cyan:#06b6d4;
  --accent-blue:#3b82f6; --accent-emerald:#10b981;
  --text-white:#ffffff; --text-light:#f0f0f5; --text-body:#c4c4d4; --text-muted:#6b6b8a;
  --glow-violet:rgba(124,58,237,.4); --glow-cyan:rgba(6,182,212,.3);
  --line:rgba(124,58,237,.18);
  --maxw:1120px; --bar:62px;
}
@font-face{
  font-family:InterVar;
    /* Vendored INTO this folder so the page is fully self-contained: it can be
     served from a domain root with one asset location and no dependency on a
     sibling site's directory. Resolves relative to this stylesheet. */
  src:url('fonts/InterVariable.ttf') format('truetype-variations');
  font-weight:100 900; font-display:swap;
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0; background:var(--bg-void); color:var(--text-body);
  font:16px/1.65 InterVar,Inter,system-ui,-apple-system,"Segoe UI",sans-serif;
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
a{color:#62dcec;text-decoration:none}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 28px}

/* --- TOP BAR — login sits upper right ---------------------------------- */
.bar{position:fixed;top:0;left:0;right:0;height:var(--bar);z-index:40;
     display:flex;align-items:center;justify-content:space-between;
     padding:0 22px;backdrop-filter:blur(14px);
     background:rgba(5,5,16,.72);border-bottom:1px solid rgba(124,58,237,.14)}
/* The brand lock-up must outrank the sign-in control. It did not: brand type was
   13px against the button's 13.5px, so the secondary action was literally the
   larger of the two. Sizes are set as a RATIO to the button rather than as two
   independent numbers, so the relationship cannot silently invert again. */
.bar-brand{display:flex;align-items:center;gap:12px;
           font-size:calc(var(--bar-login-size) * 1.35);
           letter-spacing:.13em;color:var(--text-white);font-weight:400}
.bar-brand span{color:var(--accent-purple)}
/* Pointed form at 34px. See the note in the markup: the old size-driven rule
   argued for the ball BECAUSE the mark was 22px, and the mark is no longer 22px. */
.bar-mark{width:34px;height:34px;flex:0 0 auto}
.bar-login{display:inline-flex;align-items:center;gap:8px;
           padding:8px 18px;border-radius:7px;
           border:1px solid rgba(124,58,237,.45);background:rgba(124,58,237,.14);
           color:var(--text-white)!important;font-size:var(--bar-login-size);font-weight:500;
           transition:background .3s,box-shadow .3s,border-color .3s}
.bar-login:hover{background:rgba(124,58,237,.30);box-shadow:0 0 26px var(--glow-violet);
                 border-color:rgba(168,85,247,.7);text-decoration:none}
.bar-login svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;
               stroke-linecap:round;stroke-linejoin:round}
@media(max-width:520px){
  .bar-brand span{display:none}
  .bar-login{padding:8px 14px}
}

/* --- HERO ---------------------------------------------------------------- */
.hero{position:relative;min-height:100svh;display:flex;flex-direction:column;
      align-items:center;justify-content:center;text-align:center;
      padding:calc(var(--bar) + 60px) 0 72px;overflow:hidden}
/* The burst is centred ON the mark so the logo reads as the singularity at the
   heart of the star, not as a lost dot inside it.

   THE MASK IS NOW A FLOOR, NOT A CROP. The previous mask cut the field to a
   62% x 42% ellipse, which is what made the figure an ornament sitting in a box.
   It was there for a real reason: glowing gradient discs landed on top of "WEB
   SERVICES" and across the body copy. Those discs are gone — tips are now solid
   sub-2px points — so the field can reach the edges of the screen while the mask
   does nothing but hold brightness down behind the type block. It never reaches
   transparent, so the field is continuous to every edge. */
#starburst{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;
           z-index:0;opacity:.95;
           -webkit-mask-image:radial-gradient(ellipse 58% 40% at 50% 34%,
                     rgba(0,0,0,.34) 0%, rgba(0,0,0,.42) 30%,
                     rgba(0,0,0,.78) 56%, #000 82%);
           mask-image:radial-gradient(ellipse 58% 40% at 50% 34%,
                     rgba(0,0,0,.34) 0%, rgba(0,0,0,.42) 30%,
                     rgba(0,0,0,.78) 56%, #000 82%)}
.hero-inner{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center}

.mark{width:150px;height:150px;margin-bottom:2px}
@media(max-width:640px){.mark{width:112px;height:112px}}

/* At phone width the text block is a larger share of the screen, so the readable
   floor behind it is held a little lower and a little higher up the hero. Still a
   floor, never a crop. */
@media(max-width:640px){
  #starburst{
    -webkit-mask-image:radial-gradient(ellipse 78% 26% at 50% 27%,
              rgba(0,0,0,.26) 0%, rgba(0,0,0,.34) 34%,
              rgba(0,0,0,.72) 58%, #000 84%);
    mask-image:radial-gradient(ellipse 78% 26% at 50% 27%,
              rgba(0,0,0,.26) 0%, rgba(0,0,0,.34) 34%,
              rgba(0,0,0,.72) 58%, #000 84%)}
}

.wordmark{font-size:clamp(25px,5vw,42px);font-weight:200;letter-spacing:.14em;
          color:var(--text-white);margin:8px 0 0;line-height:1.2}
.wordmark span{color:var(--accent-purple);font-weight:400}

.soon{display:inline-block;margin-top:24px;padding:9px 22px;border-radius:999px;
      border:1px solid var(--line);background:rgba(124,58,237,.10);
      color:var(--text-light);font-size:12px;letter-spacing:.24em;text-transform:uppercase}

.lede{max-width:660px;margin:28px auto 0;font-size:clamp(16px,1.9vw,19px);
      line-height:1.7;color:var(--text-body)}
/* The second approved hero line. Held one step quieter than the first so the
   opening viewport still says ONE thing, with the second line as its support. */
.lede-2{margin-top:14px;font-size:clamp(15px,1.7vw,17.5px);color:var(--text-muted)}

.scroll-cue{margin-top:46px;color:var(--text-muted);font-size:12px;letter-spacing:.18em;
            text-transform:uppercase}
.scroll-cue svg{display:block;margin:10px auto 0;opacity:.6;animation:bob 2.4s ease-in-out infinite}
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(6px)}}

/* --- SECTIONS ------------------------------------------------------------ */
section{padding:86px 0;border-top:1px solid rgba(124,58,237,.10)}
.eyebrow{font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:#62dcec;
         margin:0 0 14px}
h2{font-size:clamp(25px,3.4vw,40px);font-weight:200;line-height:1.2;letter-spacing:-.9px;
   color:var(--text-white);margin:0 0 20px}
h2 em{font-style:normal;display:block;color:var(--accent-purple)}
.section-copy{max-width:700px;font-size:17px;line-height:1.75}
.section-copy + .section-copy{margin-top:16px}

/* Scope note — used wherever a statement could be misread as shipped-today or as
   a claim about THIS preview site. Deliberately quiet but present. */
/* These notices now sit over artwork rather than over flat ground, so the panel
   that carries them has to supply its own contrast. Both are scope statements
   that must stay legible — the wallet one in particular says post-quantum
   protection does NOT extend to Bitcoin's or Ethereum's own cryptography, and a
   sentence like that going quiet against a background is a real fault, not a
   cosmetic one. Opaque ground, brighter ink, slight blur behind. */
.scope{margin-top:20px;padding:14px 18px;border-left:2px solid rgba(168,85,247,.6);
       background:rgba(8,8,22,.82);border-radius:0 8px 8px 0;
       -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
       font-size:13.5px;line-height:1.6;color:var(--text-light);max-width:700px}

/* --- CAPABILITY FIGURES --------------------------------------------------
   Each capability is drawn in the brand's own light language — one shared
   gradient vocabulary, one optical weight, a white core with violet halo and
   cyan falloff. They are a set because they are built from one system, not
   because they were downloaded together. Stock line icons would not be.
   ------------------------------------------------------------------------- */
/* MEASURED DEFECT, phone at 390px: minmax(210px,1fr) could not fit two columns in
   the 334px of available width, so the grid collapsed to ONE column and produced
   seven 334x206 blocks — 1,442px of tiles carrying seven words. That is the
   "giant tiles with little words" fault exactly. The minimum is now small enough
   that two columns always fit at phone width, and the cell is sized by its
   CONTENT instead of by padding around a lot of empty space. */
.caps{display:grid;gap:10px;margin-top:34px;
      grid-template-columns:repeat(auto-fit,minmax(142px,1fr))}
.cap{display:flex;flex-direction:column;align-items:center;justify-content:center;
     gap:10px;padding:18px 12px;text-align:center;
     border:1px solid rgba(124,58,237,.16);border-radius:12px;
     background:linear-gradient(180deg,rgba(22,22,58,.5),rgba(10,10,26,.3));
     transition:border-color .3s,transform .3s,background .3s}
.cap:hover{border-color:rgba(168,85,247,.42);transform:translateY(-3px);
           background:linear-gradient(180deg,rgba(28,24,70,.6),rgba(10,10,26,.35))}
/* The figure grows as the paragraph goes: with no body text the icon IS the cell,
   so it carries the weight the text used to. */
.cap figure{margin:0;width:46px;height:46px}
.cap h3{margin:0;font-size:14px;font-weight:500;color:var(--text-white);
        letter-spacing:.03em}
/* Seven cells in a two-column grid leaves an orphan on the last row — measured
   as rows of [2,2,2,1]. The odd one out spans the full width instead, so the set
   ends on a deliberate row rather than a gap. Scoped below 900px because the
   desktop grid is seven across and has no orphan to solve. */
@media(max-width:899px){
  .cap:last-child:nth-child(odd){grid-column:1/-1;flex-direction:row;gap:14px;padding:14px 18px}
  .cap:last-child:nth-child(odd) figure{width:38px;height:38px}
}
@media(min-width:900px){
  .caps{grid-template-columns:repeat(7,1fr)}   /* one row of seven at desk width */
  .cap figure{width:52px;height:52px}
}

/* One-line coming-soon frame. Replaces a four-line block: the hero says COMING
   SOON and that governs the page. */
.frame-line{margin:18px 0 0;font-size:14px;color:var(--text-muted);max-width:640px}

/* --- ARTWORK AS SECTION CONTEXT -------------------------------------------
   Every piece is the same artwork, at full quality, unmodified. What changed is
   its RELATIONSHIP to the words: behind them and faded into the page rather than
   dropped in as a standalone panel between paragraphs. Never allowed to compete
   with body copy — it sits under a fade to the page ground on both edges. */
.art{position:relative;isolation:isolate}
/* FULL-BLEED, done structurally. `.art` is now the full-width OUTER section with a
   `.wrap` inside carrying the content, so the pseudos are simply width:100% of a
   full-width parent.
   The earlier version broke out with 100vw from inside the constrained column —
   which works, but 100vw INCLUDES a classic scrollbar, so it overflowed by ~15px
   on any platform without overlay scrollbars and needed `overflow-x:clip` to hide
   it. This has no scrollbar term in it at all, so there is nothing left to mask. */
.art::before,.art::after{left:0;right:0;width:auto}
.art::before{content:"";position:absolute;top:-86px;bottom:-86px;z-index:-1;
  background-image:var(--art);background-size:cover;background-position:center;
  opacity:.34;
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 22%,#000 78%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 22%,#000 78%,transparent)}
.art::after{content:"";position:absolute;top:-86px;bottom:-86px;z-index:-1;
  background:radial-gradient(ellipse 74% 62% at 50% 50%,
             rgba(5,5,16,.86) 0%, rgba(5,5,16,.62) 46%, rgba(5,5,16,.30) 100%)}
#caps{--art:url(services-v1.jpg)}
#devs{--art:url(developers-v1.jpg)}
#wallet{--art:url(wallet-v1.jpg)}
@media(prefers-reduced-motion:reduce){.art::before{opacity:.26}}

/* --- DEVELOPERS ----------------------------------------------------------- */
.dev-points{display:grid;gap:16px;margin-top:38px;
            grid-template-columns:repeat(auto-fit,minmax(262px,1fr))}
.dev{padding:24px;border-radius:12px;border:1px solid rgba(6,182,212,.16);
     background:linear-gradient(180deg,rgba(6,40,58,.34),rgba(10,10,26,.3))}
.dev h3{margin:0 0 9px;font-size:15.5px;font-weight:500;color:var(--text-white)}
.dev p{margin:0;font-size:14px;line-height:1.62;color:var(--text-body)}

/* --- LOGIN --------------------------------------------------------------- */
.login{text-align:center;padding:92px 0 104px}
.login .section-copy{margin-left:auto;margin-right:auto}
.btn{display:inline-flex;align-items:center;gap:10px;margin-top:26px;
     padding:14px 30px;border-radius:9px;border:1px solid rgba(124,58,237,.5);
     background:rgba(124,58,237,.16);color:var(--text-white)!important;
     font-size:15px;font-weight:500;
     transition:background .3s,box-shadow .3s,border-color .3s}
.btn:hover{background:rgba(124,58,237,.30);box-shadow:0 0 34px var(--glow-violet);
           border-color:rgba(168,85,247,.75);text-decoration:none}
.btn svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:2;
         stroke-linecap:round;stroke-linejoin:round}
.login-note{margin-top:18px;font-size:13px;color:var(--text-muted)}

footer{border-top:1px solid rgba(124,58,237,.10);padding:30px 0 46px;
       font-size:12.5px;color:var(--text-muted);text-align:center}

/* --- FREE / OPEN + LAUNCH NOTIFICATION ------------------------------------
   Free and open must be unmistakable. The signup is a LAUNCH NOTIFICATION —
   not presales, not a newsletter — so the copy says exactly that and the form
   asks for one field and nothing else.
   ------------------------------------------------------------------------- */
.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.free-line{margin:18px 0 0;font-size:15px;letter-spacing:.02em;color:var(--accent-purple)}

/* Third-party pricing note. Deliberately NOT .scope and NOT muted: body size,
   full contrast, directly under the cards it qualifies. Never fine print. */
.pricing-line{margin:24px 0 0;max-width:none;font-size:16px;line-height:1.7;
  color:var(--text-light);padding:14px 18px;border-radius:10px;
  border:1px solid rgba(6,182,212,.28);background:rgba(6,182,212,.07)}


.join{text-align:center;padding:92px 0}
.join h2{margin-bottom:16px}
.join .section-copy{margin-left:auto;margin-right:auto}
.join-free{display:inline-block;margin-bottom:22px;padding:10px 24px;border-radius:999px;
           border:1px solid rgba(16,185,129,.4);background:rgba(16,185,129,.10);
           color:#6ee7b7;font-size:13px;letter-spacing:.08em}

.signup{margin:32px auto 0;max-width:520px}
.signup-row{display:flex;gap:10px;flex-wrap:wrap;justify-content:center}
.signup input[type=email]{flex:1 1 260px;min-width:0;padding:13px 16px;border-radius:9px;
  border:1px solid rgba(124,58,237,.34);background:rgba(10,10,26,.8);
  color:var(--text-white);font-size:15px;transition:border-color .25s,box-shadow .25s}
.signup input[type=email]::placeholder{color:var(--text-muted)}
.signup input[type=email]:focus{outline:none;border-color:rgba(168,85,247,.8);
  box-shadow:0 0 0 3px rgba(124,58,237,.18)}
.signup input[type=email][aria-invalid="true"]{border-color:rgba(255,82,82,.75);
  box-shadow:0 0 0 3px rgba(255,23,68,.14)}
.signup button{flex:0 0 auto;padding:13px 24px;border-radius:9px;cursor:pointer;
  border:1px solid rgba(124,58,237,.5);background:rgba(124,58,237,.2);
  color:var(--text-white);font-size:15px;font-weight:500;
  transition:background .3s,box-shadow .3s,border-color .3s,opacity .3s}
.signup button:hover:not(:disabled){background:rgba(124,58,237,.34);
  box-shadow:0 0 30px var(--glow-violet);border-color:rgba(168,85,247,.75)}
.signup button:disabled{opacity:.55;cursor:not-allowed}
.signup-note{margin:14px 0 0;font-size:12.5px;color:var(--text-muted)}
/* One live region carries every outcome, so a screen reader hears the result
   rather than only sighted users seeing a colour change. */
.signup-msg{margin:16px 0 0;font-size:14px;line-height:1.55;min-height:1.2em}
.signup-msg[data-state="error"]{color:#ff8a8a}
.signup-msg[data-state="pending"]{color:var(--text-muted)}
.signup-msg[data-state="ok"]{color:#6ee7b7}
.signup-msg[data-state="unconfigured"]{color:#ffd37a}

/* --- ARTWORK -------------------------------------------------------------
   Conceptual artwork, not protocol diagrams. Decorative, so alt="" —
   descriptive alt text would assert they depict the architecture. aspect-ratio
   is declared so the reserved box matches the asset and the page does not shift
   as images decode.
   ------------------------------------------------------------------------- */
/* --- RESTRAINED SCROLL MOTION ---------------------------------------------
   A short fade and a few pixels of travel as a section arrives, once. No
   scroll-jacking, nothing that moves while you read.
   Every bit of it is off under reduced motion — see the block below, where
   .reveal is forced to its final state so content can never be stuck invisible.
   ------------------------------------------------------------------------- */
.reveal{opacity:0;transform:translateY(14px);
  transition:opacity .6s ease-out, transform .6s ease-out}
.reveal.in{opacity:1;transform:none}

/* --- REDUCED MOTION ------------------------------------------------------
   NOTE: the live homepage has NO prefers-reduced-motion handling at all — this
   is ADDED here, not inherited. The canvas also checks the same query in JS and
   renders ONE still frame instead of animating, because a CSS rule cannot stop a
   requestAnimationFrame loop.
   ------------------------------------------------------------------------- */
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
                       transition-duration:.001ms!important}
  .scroll-cue svg{animation:none}
  /* Content must NEVER depend on an animation having run. If the observer never
     fires, or motion is reduced, .reveal is simply its finished state. */
  .reveal{opacity:1!important;transform:none!important;transition:none!important}
}

/* --- PHONE RHYTHM ---------------------------------------------------------
   Section padding was authored at desktop (86px, and 92px on the two centred
   blocks) and carried unchanged to a 844px-tall screen, where it reads as empty
   space rather than as breathing room. Measured: the invitation and sign-in
   blocks were 530px and 519px tall for a form and a button. */
@media(max-width:640px){
  /* NOTE: a `section{padding:...}` rule here would do NOTHING. `.wrap` sets the
     padding SHORTHAND (`padding:0 28px`) with higher specificity and later in the
     file, so it zeroes the vertical padding of every section on this page. The
     existing `section{padding:86px 0}` above is dead for exactly that reason and
     has never applied — the page's vertical rhythm comes entirely from child
     margins. Left alone deliberately: making it live would add 172px to every
     section, which is a page-wide change nobody asked for and the opposite of the
     direction here. Flagged rather than silently "fixed". */
  .login{padding:56px 0 64px}
  .join{padding:56px 0}
  .art::before,.art::after{top:-54px;bottom:-54px}
}

/* --- SECTION RHYTHM -------------------------------------------------------
   Sections on this page have zero vertical padding — `.wrap`'s shorthand zeroes
   it and always has. Rather than revive `section{padding:86px 0}` and add 172px
   everywhere, the separation is put on the artwork sections' inner wrapper, which
   nothing else competes with. Measured symptom: at 390px the FOR DEVELOPERS
   eyebrow sat directly against the pricing panel above it with no gap at all. */
.art > .wrap{padding-top:46px;padding-bottom:46px}
@media(min-width:900px){.art > .wrap{padding-top:64px;padding-bottom:64px}}

/* The short wallet scope line. Body size and full contrast, not fine print: it is
   the sentence that bounds what post-quantum protection covers. */
.scope-line{margin:20px 0 0;max-width:700px;font-size:14.5px;line-height:1.65;
  color:var(--text-light);padding-left:14px;border-left:2px solid rgba(168,85,247,.6)}
