/* ═══════════════════════════════════════════════════════════════════════════
   QYVERIX CLOUD — MARKETING LAYER
   The signed-out surface at cloud.qyverix.in/.

   Loaded AFTER qyverix.css and additive only. It defines nothing the product
   UI uses and overrides exactly one app rule (.page, scoped to .mkt), so a
   change here cannot regress a dashboard screen. /_design stays authoritative
   for app components.

   Why this file exists at all
   ---------------------------
   qyverix.css is dense by design: 13px body, 40px rows, 32px section gaps.
   That register is correct for someone who lives in the panel all day and
   wrong for someone deciding whether to sign up. Marketing needs editorial
   scale and real negative space. Rather than fight the app scale with
   overrides scattered through a view, the marketing register is declared once
   here, scoped under .mkt.

   Typography is the other reason. The app is Inter + JetBrains Mono. The
   brand public surfaces -- qyverix.in and srv.qyverix.in -- are Cormorant
   display over Cinzel caps on gold-and-near-black. This page is the seam
   between those two worlds, so it carries the brand display faces and hands
   off to Inter at the sign-in boundary. Same argument qyverix.css already
   makes about the palette: two type systems across the signup boundary would
   read as two companies.

   Motion rules this file holds itself to
   --------------------------------------
   1. Only `transform` and `opacity` are animated. Both composite; neither
      triggers layout or paint. Every keyframe below is annotated.
   2. Nothing animates on load except the hero, and the hero resolves within
      one second. A visitor must never wait on choreography to read a price.
   3. Every effect degrades to its final state under prefers-reduced-motion,
      enforced by the blanket rule at the bottom of this file.
   ═══════════════════════════════════════════════════════════════════════════ */

@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600&family=Cinzel:wght@600&display=swap');

/* ─────────────────────────────────────────────────────── marketing scale ── */

.mkt {
  /* Display faces, marketing only. --sans and --mono are inherited unchanged
     so body copy and every technical value still match the product exactly. */
  --display: 'Cormorant Garamond', 'EB Garamond', Georgia, serif;
  --caps:    'Cinzel', 'Cormorant Garamond', Georgia, serif;

  /* Editorial steps. These sit ABOVE the app scale rather than replacing it;
     --t-* still resolves for anything reusing an app component inline. */
  --m-hero:  clamp(2.75rem, 7.2vw, 5.75rem);
  --m-xl:    clamp(2rem, 4.4vw, 3.25rem);
  --m-lg:    clamp(1.5rem, 2.6vw, 2rem);
  --m-lead:  clamp(1rem, 1.35vw, 1.1875rem);
  --m-body:  15px;

  --m-wrap:  1180px;   /* narrower than --content-max: prose, not tables   */
  --m-prose: 62ch;     /* measure cap, applied to every paragraph          */
  --m-gap:   clamp(4.5rem, 11vh, 8.5rem);  /* space between chapters       */

  font-size: var(--m-body);
  line-height: 1.62;
}

/* The router wraps a view in .page unless the returned node already carries
   it. home.js returns .page.mkt, so this releases the dashboard container:
   marketing sections need to reach the viewport edge. */
.page.mkt { max-width: none; padding: 0; }

/* ─────────────────────────────────────────────────────────────── ground ── */

/* Two fixed radial washes behind everything. This is the technique public.html
   already uses and it is load-bearing: without it #050508 across a full
   viewport reads as flat dead grey rather than as depth. Fixed, not scrolling,
   so it costs one paint for the life of the page. */
.mkt-bg {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(58% 44% at 82% -8%,  var(--accent-wash), transparent 68%),
    radial-gradient(52% 40% at 4% 104%,  var(--info-wash),   transparent 70%);
}

/* Hairline grid, masked to a soft pool. Reads as drafting paper rather than as
   decoration, and disappears entirely before it can compete with type. */
.mkt-grid-overlay {
  position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(var(--border) 1px, transparent 1px),
    linear-gradient(90deg, var(--border) 1px, transparent 1px);
  background-size: 56px 56px;
  -webkit-mask-image: radial-gradient(58% 66% at 28% 34%, #000, transparent 76%);
          mask-image: radial-gradient(58% 66% at 28% 34%, #000, transparent 76%);
  opacity: .6;
}

/* ────────────────────────────────────────────────────────────── chrome ── */

.mkt-wrap {
  width: 100%; max-width: var(--m-wrap);
  margin-inline: auto; padding-inline: var(--s6);
}

.mkt-sec { position: relative; padding-block: var(--m-gap); }
.mkt-sec + .mkt-sec { border-top: 1px solid var(--border); }

/* Numbered chapters, the convention public.html already established:
   mono gold ordinal, then a caps heading. */
.mkt-chapter {
  display: flex; align-items: baseline; gap: var(--s4);
  margin-bottom: var(--s8);
}
.mkt-num {
  font-family: var(--mono); font-size: var(--t-12);
  color: var(--accent); letter-spacing: .12em; flex: none;
  font-variant-numeric: tabular-nums;
}
.mkt-eyebrow {
  font-family: var(--caps);
  font-size: var(--t-11); font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--text-2);
}

h1.mkt-h, h2.mkt-h {
  font-family: var(--display);
  font-weight: 500; letter-spacing: -.02em; line-height: 1.06;
  text-wrap: balance; color: var(--text-1);
}
h1.mkt-h { font-size: var(--m-hero); }
h2.mkt-h { font-size: var(--m-xl); }
/* Sans rather than the display face: these are subheads inside a section, and
   at 2rem the serif would compete with the chapter headings above them.
   line-height is set explicitly because .mkt sets 1.62 for body copy, which
   opened a visible gap between the two lines of a wrapped subhead. */
h3.mkt-h {
  font-family: var(--sans); font-size: var(--m-lg);
  font-weight: 600; letter-spacing: -.01em; line-height: 1.16;
}

.mkt-lead {
  font-size: var(--m-lead); line-height: 1.6;
  color: var(--text-2); max-width: var(--m-prose);
}
.mkt p { max-width: var(--m-prose); }

/* ──────────────────────────────────────────────────────────────── hero ── */

.mkt-hero { padding-block: clamp(4rem, 12vh, 8rem) var(--m-gap); position: relative; overflow: hidden; }
.mkt-hero .mkt-wrap { position: relative; }

.mkt-hero-actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s8); }

/* Instrumentation strip. Deliberately small, mono and tabular: it reads as a
   status line, not as a feature list. */
.mkt-instr {
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s6);
  margin-top: var(--s9); padding-top: var(--s5);
  border-top: 1px solid var(--border);
  font-family: var(--mono); font-size: var(--t-11);
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-3); font-variant-numeric: tabular-nums;
}
.mkt-instr b { color: var(--text-2); font-weight: 500; }

/* ────────────────────────────────────────────────── state machine (SVG) ── */

/* Class-only styling, no inline fills, so the whole diagram themes off the
   tokens in both light and dark. Same construction as the board schematic in
   status.html -- that file is the house language for technical diagrams and
   this is deliberately its sibling, not a new invention. */
.fsm { width: 100%; height: auto; display: block; }
.fsm-box   { fill: var(--surface-1); stroke: var(--border-strong); stroke-width: 1; }
/* Gold, not green. Rule 1 of qyverix.css reserves the accent for current state
   and the status colours for a separate axis: a stage the reader has scrolled
   past is "reached", which is a current-state idea, not a health reading. Using
   --live here would say the build step is healthy, which is not what is meant. */
.fsm-box.is-on { stroke: var(--accent-ui); }
.fsm-label {
  font-family: var(--mono); font-size: 9px; letter-spacing: .1em;
  fill: var(--text-2); text-transform: uppercase;
}
.fsm-label.is-lit { fill: var(--accent); }
.fsm-edge  { fill: none; stroke: var(--border-strong); stroke-width: 1.2; }
.fsm-head  { fill: var(--border-strong); }

/* The return edge. Drawn in accent rather than in the neutral stroke because
   it is the one path most hosting diagrams omit, and it is what the product
   actually does. */
.fsm-edge.is-return { stroke: var(--accent-ui); stroke-dasharray: 4 3; }
.fsm-head.is-return { fill: var(--accent-ui); }

/* Signal sweep along an edge. `stroke-dashoffset` is not a compositor
   property, but it repaints a 1px path inside a small box -- cheaper here than
   the equivalent transform on a masked overlay, and it is the same technique
   status.html already ships. */
.fsm-pulse { stroke: var(--accent); stroke-width: 1.6; fill: none; stroke-dasharray: 46; }
@media (prefers-reduced-motion: no-preference) {
  .fsm-pulse { animation: fsm-sweep 3.4s var(--ease) infinite; }
}
@keyframes fsm-sweep {
  0%   { stroke-dashoffset: 46; opacity: .18; }
  50%  { opacity: 1; }
  100% { stroke-dashoffset: 0;  opacity: .18; }
}

/* ──────────────────────────────────────────────────────── product frame ── */

/* A window around real product markup. The chrome is a frame, not a fake
   browser: no URL bar, no traffic lights, nothing implying a screenshot of
   something that does not exist. */
.mkt-frame {
  border: 1px solid var(--border-strong); border-radius: var(--r4);
  background: var(--surface-1); overflow: hidden; box-shadow: var(--shadow-2);
}
.mkt-frame-bar {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  font-family: var(--mono); font-size: var(--t-11);
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-3);
}
.mkt-frame-body { padding: var(--s5); }

/* The demo rows carry five short values on one line. .row is nowrap and those
   values cannot shrink below their text, so at 375px they pushed the document
   wider than the viewport -- which then stretched the fixed background and the
   diagram container with it. Wrapping is the right fix rather than clipping:
   these are labelled values, not table columns with alignment to preserve. */
.mkt-frame .row { flex-wrap: wrap; row-gap: var(--s2); }

/* Demo marker. Any panel showing values that are not this visitor's real data
   carries one. Non-negotiable: it is the difference between illustrating a
   product and fabricating one. */
.mkt-demo {
  margin-left: auto; font-family: var(--mono); font-size: var(--t-11);
  letter-spacing: .1em; color: var(--text-3);
  border: 1px solid var(--border-strong); border-radius: var(--rp);
  padding: 1px var(--s2);
}

/* ─────────────────────────────────────────────────────────────── layout ── */

.mkt-split {
  display: grid; gap: var(--s10) var(--s9);
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
}
@media (min-width: 900px) {
  .mkt-split { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
  /* Deliberate asymmetry, and it alternates down the page so the eye is not
     dragged down a single gutter. */
  .mkt-split.is-flipped { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  .mkt-split.is-flipped > :first-child { order: 2; }
}

.mkt-cols { display: grid; gap: var(--s6); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 760px) {
  .mkt-cols.is-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .mkt-cols.is-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Fact, not testimonial. Used by the trust section, where a claim the reader
   can verify replaces social proof we do not truthfully have. */
.mkt-fact {
  border-left: 2px solid var(--accent-line);
  padding-left: var(--s5);
}
.mkt-fact h4 { font-size: var(--t-14); font-weight: 600; margin-bottom: var(--s2); }
.mkt-fact p  { font-size: var(--t-13); color: var(--text-2); }

/* Boundaries section: what the product is not. Rendered in the same visual
   weight as the feature sections, because burying it would defeat it. */
.mkt-bound { display: flex; gap: var(--s3); align-items: flex-start; }
.mkt-bound .k {
  font-family: var(--mono); font-size: var(--t-11); letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-3);
  border: 1px solid var(--border); border-radius: var(--r1);
  padding: 2px var(--s2); flex: none; margin-top: 2px;
}

/* ───────────────────────────────────────────────────────────── pricing ── */

.mkt-plan {
  border: 1px solid var(--border); border-radius: var(--r4);
  background: var(--surface-1); padding: var(--s6);
  display: flex; flex-direction: column; gap: var(--s4);
}
.mkt-plan.is-featured { border-color: var(--accent-line); }
.mkt-price {
  font-family: var(--display); font-size: var(--m-lg); font-weight: 600;
  color: var(--text-1); font-variant-numeric: tabular-nums;
}
.mkt-price .per { font-family: var(--sans); font-size: var(--t-12); color: var(--text-3); font-weight: 400; }

/* icon() emits an <svg> carrying no width or height, so every component in
   qyverix.css sizes it itself (.btn svg, .nav-item svg, .field-error svg...).
   An unsized SVG in a flex row falls back to the CSS default 300x150, which is
   what pushed the plan list 55px past the right edge of a 375px viewport and
   stretched the whole document with it. Sized here for the same reason every
   other consumer sizes it. */
.mkt-tick svg { width: 15px; height: 15px; flex: none; }

/* ────────────────────────────────────────────────────────────── footer ── */

.mkt-foot { border-top: 1px solid var(--border); padding-block: var(--m-gap) var(--s9); }
/* A lead block plus two link columns. auto-fit alone stretched two columns
   across the full 1180px and read as an unfinished page; the lead column gives
   the row something to balance against without inventing links to pages that
   do not exist. */
.mkt-foot-cols {
  display: grid; gap: var(--s8) var(--s6);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 760px) {
  .mkt-foot-cols { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr); }
}
.mkt-foot-lead p { max-width: 42ch; }
.mkt-foot h4 {
  font-family: var(--caps); font-size: var(--t-11); font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--text-3); margin-bottom: var(--s4);
}
.mkt-foot a { display: block; padding-block: 5px; color: var(--text-2); font-size: var(--t-13); }
.mkt-foot a:hover { color: var(--text-1); }

/* The wordmark closes the page. Low opacity so it reads as a printed edge
   rather than as a headline competing with the CTA above it. */
.mkt-mark {
  font-family: var(--caps); font-weight: 600;
  font-size: clamp(3rem, 15vw, 11rem); line-height: .9;
  letter-spacing: -.02em; color: var(--text-1); opacity: .07;
  margin-top: var(--s10); user-select: none;
}

/* Status label for anything not generally available. Never omitted. */
.mkt-status {
  font-family: var(--mono); font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-3);
  border: 1px solid var(--border); border-radius: var(--rp);
  padding: 1px 6px; margin-left: var(--s2); white-space: nowrap;
}

/* ───────────────────────────────────────────────────────────── reveals ── */

/* transform + opacity only: both composite, neither triggers layout. The
   initial hidden state is applied by JS, never in this file, so a visitor with
   JS disabled or a failed module load sees fully-formed content rather than a
   page of invisible sections. That failure mode is the entire reason for the
   [data-reveal] attribute hook. */
@media (prefers-reduced-motion: no-preference) {
  .mkt [data-reveal="pending"] { opacity: 0; transform: translate3d(0, 14px, 0); }
  .mkt [data-reveal="shown"] {
    opacity: 1; transform: none;
    transition: opacity 620ms var(--ease), transform 620ms var(--ease);
  }
}

/* router.js moves focus to the page heading after every navigation so a
   keyboard or screen-reader user is not stranded at the top of the chrome. The
   element it focuses carries tabindex="-1", so it is reachable programmatically
   and never by Tab -- which means the ring communicates nothing to anyone who
   put it there, and on first load it painted a 2px gold box around the whole
   hero. Suppressed only for that programmatic target, and only on marketing;
   every genuinely focusable control keeps the global ring untouched. */
.mkt [tabindex="-1"]:focus-visible { outline: none; }

/* Link underline that grows from the left. Animating scaleX composites;
   animating width or background-size would not. */
.mkt-link {
  color: var(--text-1); position: relative; text-decoration: none;
  display: inline-flex; align-items: center; gap: var(--s2);
}
.mkt-link::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -2px; height: 1px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform var(--base) var(--ease);
}
.mkt-link:hover::after, .mkt-link:focus-visible::after { transform: scaleX(1); }
.mkt-link .arw { transition: transform var(--base) var(--ease); }
.mkt-link:hover .arw { transform: translate3d(3px, 0, 0); }

/* ────────────────────────────────────────────────────────────── header ── */

.auth-bar .mkt-nav { display: none; gap: var(--s5); margin-left: var(--s7); }
@media (min-width: 860px) { .auth-bar .mkt-nav { display: flex; } }
.auth-bar .mkt-nav a { font-size: var(--t-13); color: var(--text-2); }
.auth-bar .mkt-nav a:hover { color: var(--text-1); }

/* ───────────────────────────────────────────────────────── small screens ── */

@media (max-width: 640px) {
  /* The diagram is the one element that cannot simply reflow: below ~600px
     the edge labels collide. It scrolls in its own track rather than
     shrinking into illegibility. */
  .fsm-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .fsm-scroll .fsm { min-width: 560px; }
  .mkt-hero-actions .btn { width: 100%; }
}

/* Honours the same blanket rule qyverix.css applies. Restated here because
   this file introduces its own keyframes and transitions, and a reader
   auditing motion should find the guarantee in the file that made the
   promise. */
@media (prefers-reduced-motion: reduce) {
  .mkt *, .mkt *::before, .mkt *::after {
    animation: none !important;
    transition: none !important;
  }
  .mkt [data-reveal] { opacity: 1 !important; transform: none !important; }
}
