/* ============================================================
   VEXT LABS · Brand direction "Proposal A" (Yahya Vision, creative
   direction phase) — proposal-a.css
   Packet: BRAND-PROPOSAL-A-20260819

   Founder decision 2026-08-19: this direction SUPERSEDES Porcelain
   Futurism as the governing brand. Porcelain / LUXLIQ / FACET / Meadow
   are lineage. Sequencing: vextlabs.ai first, then juwel.ai marketing,
   the OS shell last (after 4 September) — restyling the live product on
   launch week is where things break.

   Mark: Figma X (node 1:1337) + Helvetica Now “Vext Labs” (node 1:1220).
   Colour and type tokens live in vext-brand.css (Figma 2026-09-01).
   This file keeps Porcelain spacing, mosaic, and component grammar.

   VARIABLE-FIRST. Every colour, size and family below is a token. No
   component in this system may hardcode a value; swapping the direction
   must be an edit to this file alone.
   ============================================================ */

@import url("vext-brand.css");

:root {
  /* Colour + type: vext-brand.css (Figma 2026-09-01). Space/scale stay here. */

  /* Fluid display scale. Ratio holds at every width, so the hero never
     needs a per-breakpoint override. */
  --pa-step--1: clamp(0.82rem, 0.80rem + 0.10vw, 0.90rem);
  --pa-step-0:  clamp(1.00rem, 0.96rem + 0.18vw, 1.12rem);
  --pa-step-1:  clamp(1.25rem, 1.15rem + 0.45vw, 1.55rem);
  --pa-step-2:  clamp(1.60rem, 1.35rem + 1.10vw, 2.30rem);
  --pa-step-3:  clamp(2.10rem, 1.55rem + 2.40vw, 3.40rem);
  --pa-step-4:  clamp(2.30rem, 1.40rem + 3.80vw, 4.20rem);
  --pa-step-5:  clamp(2.90rem, 1.60rem + 5.20vw, 5.60rem);

  --pa-lh-tight: 0.98;
  --pa-lh-snug:  1.12;
  --pa-lh-body:  1.55;

  /* ---- Space, radius, motion -------------------------------------- */
  --pa-s1: 4px;  --pa-s2: 8px;  --pa-s3: 12px; --pa-s4: 16px;
  --pa-s5: 24px; --pa-s6: 32px; --pa-s7: 48px; --pa-s8: 72px;
  --pa-s9: 112px; --pa-s10: 160px;
  --pa-measure: 62ch;
  --pa-gutter: clamp(20px, 5vw, 88px);
  --pa-maxw: 1440px;

  /* The mosaic tile: a rounded square. This radius IS the brand's
     signature shape and every tile inherits it. */
  --pa-tile: 28px;
  --pa-tile-radius: 7px;
  --pa-tile-gap: 2px;

  --pa-radius-sm: 6px;
  --pa-radius:    10px;
  --pa-radius-lg: 18px;

  --pa-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ---- Base ------------------------------------------------------- */
.pa {
  background: var(--pa-bg);
  color: var(--pa-text);
  font-family: var(--pa-font-body);
  font-size: var(--pa-step-0);
  line-height: var(--pa-lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
.pa *, .pa *::before, .pa *::after { box-sizing: border-box; }

/* Inverse is a scope, not a theme: set roles once, and every component
   inside is correct without knowing where it sits. */
.pa-inverse {
  background: var(--pa-bg-inverse);
  color: var(--pa-text-inverse);
  --pa-text: var(--pa-text-inverse);
  --pa-text-muted: var(--pa-text-muted-inverse);
  --pa-hair: var(--pa-hair-inverse);
}

.pa-wrap { max-width: var(--pa-maxw); margin-inline: auto; padding-inline: var(--pa-gutter); }
.pa-section { padding-block: var(--pa-s9); }
.pa-section--tight { padding-block: var(--pa-s7); }

/* ---- Type primitives -------------------------------------------- */
.pa-display {
  font-family: var(--pa-font-display);
  font-weight: 400;
  font-size: var(--pa-step-5);
  line-height: var(--pa-lh-tight);
  letter-spacing: -0.018em;
  margin: 0;
  text-wrap: balance;
}
.pa-display--sm { font-size: var(--pa-step-4); }
.pa-h2 {
  font-family: var(--pa-font-display);
  font-weight: 400;
  font-size: var(--pa-step-3);
  line-height: var(--pa-lh-snug);
  letter-spacing: -0.012em;
  margin: 0;
  text-wrap: balance;
}
.pa-lede {
  font-size: var(--pa-step-1);
  line-height: 1.45;
  max-width: 46ch;
  margin: 0;
  color: var(--pa-text-muted);
  text-wrap: pretty;
}
.pa-body { max-width: var(--pa-measure); margin: 0; text-wrap: pretty; }
.pa-eyebrow {
  font-family: var(--pa-font-mono);
  font-size: var(--pa-step--1);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--pa-text-muted);
  margin: 0;
}
/* Used sparingly. In the direction it appears exactly once, as an aside. */
.pa-script {
  font-family: var(--pa-font-script);
  font-size: var(--pa-step-1);
  letter-spacing: 0.02em;
  color: var(--pa-text);
}

/* ---- Wordmark ---------------------------------------------------- */
/* X from Figma 1:1337. Shown via .vl-lockup in vext-brand.css. */
.pa-mark { display: block; width: 28px; height: 28px; }
.pa-wordmark {
  font-family: var(--pa-font-display);
  font-weight: 700;
  font-size: var(--pa-step-1);
  letter-spacing: var(--vb-track-display, -0.03em);
  color: var(--pa-text);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: var(--pa-s2);
  min-height: 44px;
}

/* ---- Actions — orange is the act -------------------------------- */
.pa-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pa-s2);
  min-height: 48px;                     /* comfortable touch target */
  padding: 0 var(--pa-s5);
  font: inherit;
  /* 19px/700 is not a style choice, it is the AA boundary -- see note above. */
  font-size: 1.1875rem;
  font-weight: 700;
  letter-spacing: -0.005em;
  color: var(--pa-action-text);
  background: var(--pa-action);
  border: 1px solid var(--pa-action);
  border-radius: var(--pa-radius-sm);
  cursor: pointer;
  text-decoration: none;
  transition: transform .18s var(--pa-ease), filter .18s var(--pa-ease);
}
.pa-btn:hover { filter: brightness(1.06); }
.pa-btn:active { transform: translateY(1px); }
.pa-btn:focus-visible { outline: 3px solid var(--pa-accent); outline-offset: 3px; }

.pa-btn--ghost {
  color: var(--pa-text);
  background: transparent;
  border-color: var(--pa-hair);
}
.pa-btn--ghost:hover { border-color: var(--pa-text); filter: none; }

/* ---- The mosaic -------------------------------------------------
   The signature visual: a grid of rounded tiles in orange and magenta.
   Built as a CSS grid of empty spans so it needs no image asset, no
   network request, and scales to any container. Tile colour is applied
   by juwel mosaic JS (deterministic, fixed seed) or by nth-child
   fallbacks below so it is never blank with JS off. */
.pa-mosaic {
  display: grid;
  grid-template-columns: repeat(var(--pa-mosaic-cols, 12), minmax(0, 1fr));
  gap: var(--pa-tile-gap);
  width: 100%;
  /* NO aspect-ratio here on purpose. Constraining the container's height AND
     giving each tile aspect-ratio:1 fights: the row box ends up taller than the
     square tile inside it and the grid reads as stripes with gutters between
     rows. Letting the height fall out of (cell width x row count) keeps the
     tiles touching, which is what makes it read as one mass. */
}
.pa-mosaic i {
  display: block;
  aspect-ratio: 1;
  border-radius: var(--pa-tile-radius);
  background: transparent;
}
/* The full-bleed field is the one case that must cover a box of a fixed
   height, so there the tiles stretch instead of staying square. */
.pa-field > .pa-mosaic i { aspect-ratio: auto; height: 100%; }
.pa-mosaic i[data-t="o"] { background: var(--pa-orange); }
.pa-mosaic i[data-t="m"] { background: var(--pa-accent); }
/* JS-off fallback: still reads as the brand rather than an empty box. */
.pa-mosaic:not([data-painted]) i:nth-child(3n+1)  { background: var(--pa-orange); }
.pa-mosaic:not([data-painted]) i:nth-child(4n+2)  { background: var(--pa-accent); }

/* A mosaic used as a field behind content.
   Layering is done with POSITIVE z-index on both children rather than pushing
   the art to z-index:-1. A negative-z child inside an `isolation:isolate`
   stacking context did not paint at all here -- the section rendered as an
   empty band while the DOM and geometry both measured correct, which is the
   worst kind of bug to debug. Two positive layers cannot do that. */
.pa-field { position: relative; overflow: hidden; background: var(--pa-black); }
.pa-field > .pa-mosaic {
  position: absolute; inset: 0; z-index: 0; height: 100%;
  grid-auto-rows: 1fr;   /* rows share the height, so the field always covers */
}
.pa-field > .pa-wrap { position: relative; z-index: 1; }

/* ---- Quote card, as in the direction ---------------------------- */
.pa-quote {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pa-s5);
  max-width: 760px;
  margin-inline: auto;
  padding: var(--pa-s6) var(--pa-s7);
  background: var(--pa-cream);
  color: var(--pa-black);
  border-radius: var(--pa-radius-lg);
  box-shadow: 0 18px 60px rgb(0 0 0 / 0.18);
}
.pa-quote p {
  font-family: var(--pa-font-display);
  font-size: var(--pa-step-2);
  line-height: var(--pa-lh-snug);
  letter-spacing: -0.01em;
  margin: 0;
  text-wrap: balance;
}

/* ---- Layout helpers --------------------------------------------- */
.pa-split {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--pa-s8);
  align-items: center;
}
@media (min-width: 900px) {
  .pa-split { grid-template-columns: 1.15fr 0.85fr; gap: var(--pa-s9); }
}
.pa-stack > * + * { margin-top: var(--pa-s5); }

/* A headline that should cross both columns of a .pa-split. Without this the
   hero sets 33 characters of 90px serif inside a ~600px column and wraps to
   four ragged lines. */
.pa-span-all { grid-column: 1 / -1; }
@media (min-width: 900px) { .pa-hero-copy { max-width: 46ch; } }
.pa-rule { height: 1px; background: var(--pa-hair); border: 0; margin: 0; }

/* ---- Accessibility ---------------------------------------------- */
.pa-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
.pa :focus-visible { outline: 3px solid var(--pa-accent); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .pa *, .pa *::before, .pa *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
}

/* ---- Navigation links ------------------------------------------------
   Measured at 375px: bare inline links rendered 20px tall, well under the
   44px minimum touch target, and the header nav wrapped "Open source" onto
   two lines in a 40px-wide box. These rules give every nav/footer link a
   real hit area and let the header nav wrap as a row instead of squeezing. */
.pa-nav {
  display: flex;
  align-items: center;
  gap: var(--pa-s3);
  flex-wrap: wrap;
  justify-content: flex-end;
  font-size: var(--pa-step--1);
}
.pa-nav a, .pa-navlink {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--pa-s2);
  color: var(--pa-text-muted);
  text-decoration: none;
  white-space: nowrap;
}
.pa-nav a:hover, .pa-navlink:hover { color: var(--pa-text); }
/* .pa-nav a's min-height:44px (above) otherwise beats .pa-btn's 48px on
   specificity (class+element > class) the moment a .pa-btn sits inside a
   .pa-nav — found by rendering the P1 chrome's CTA and measuring it, not
   by inspection; 44px still clears WCAG target-size guidance but was not
   the "comfortable" 48px the button's own comment promises. */
.pa-nav a.pa-btn { min-height: 48px; color: var(--pa-action-text); padding-inline: var(--pa-s4); }

@media (max-width: 560px) {
  /* Stack the header so the wordmark keeps its line and the nav gets its own. */
  .pa-header { flex-wrap: wrap; row-gap: var(--pa-s2); }
  .pa-nav { width: 100%; justify-content: flex-start; }
}

/* ============================================================
   Shared chrome — canonical nav + footer (packet P1,
   VEXTLABS-P1-TOKENS-CHROME-20260903). Markup source of truth is
   _chrome.html; every page pastes that markup byte-for-byte, so this is
   the ONLY place the header/footer/skip-link/menu-toggle presentation
   lives. A page that still carries its own inline <style> for .site-head
   or .site-foot is duplicating what belongs here — drop the duplicate
   when a page adopts _chrome.html, or the two rulesets fight on specificity.
   Every value below is an existing --pa- or --vb- token. No new colour.
   ============================================================ */

.vl-skip {
  position: fixed;
  z-index: 1000;
  top: 12px;
  left: 12px;
  padding: var(--pa-s2) var(--pa-s3);
  background: var(--pa-black);
  color: var(--pa-white);
  border-radius: var(--pa-radius-sm);
  text-decoration: none;
  transform: translateY(-180%);
  transition: transform .16s var(--pa-ease);
}
.vl-skip:focus {
  transform: translateY(0);
  outline: 3px solid var(--pa-accent);
  outline-offset: 3px;
}

.site-head { border-bottom: 1px solid var(--pa-hair); }
.site-head__in {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--pa-s5);
  row-gap: var(--pa-s2);
  min-height: 72px;
}
.site-head .pa-nav { margin-inline-start: auto; }

/* Menu button: hidden at desktop widths, where the nav already renders as
   one row (design-taste-frontend §4.7: nav on one line, <=80px tall). Drawn
   in pure CSS (three bars via box + pseudo-elements) rather than an icon
   library glyph, matching the brand's "inline SVG line icons only" imagery
   rule — this is UI chrome, not an illustrated icon. */
.site-menu-btn {
  display: none;
  position: relative;
  width: 44px;
  height: 44px;
  padding: 0;
  flex: 0 0 auto;
  border: 1px solid var(--pa-hair);
  border-radius: var(--pa-radius-sm);
  background: transparent;
  color: var(--pa-text);
  cursor: pointer;
}
.site-menu-btn span,
.site-menu-btn::before,
.site-menu-btn::after {
  position: absolute;
  top: 50%;
  left: 50%;
  display: block;
  width: 18px;
  height: 2px;
  background: currentColor;
  content: "";
  transition: transform .18s var(--pa-ease), opacity .18s var(--pa-ease);
}
.site-menu-btn span { transform: translate(-50%, -50%); }
.site-menu-btn::before { transform: translate(-50%, calc(-50% - 6px)); }
.site-menu-btn::after { transform: translate(-50%, calc(-50% + 6px)); }
.site-menu-btn[aria-expanded="true"] span { opacity: 0; }
.site-menu-btn[aria-expanded="true"]::before { transform: translate(-50%, -50%) rotate(45deg); }
.site-menu-btn[aria-expanded="true"]::after { transform: translate(-50%, -50%) rotate(-45deg); }
.site-menu-btn:focus-visible { outline: 3px solid var(--pa-accent); outline-offset: 3px; }

@media (max-width: 840px) {
  .site-menu-btn { display: inline-block; }
  /* Collapsed by default; [data-menu] starts data-open="false" in the
     markup so the nav is genuinely absent (not just visually hidden) until
     chrome.js — or a user with JS off, see chrome.js's header comment —
     opens it. Every link stays a normal in-flow <a>, so nothing here
     changes how the nav is reached by keyboard; it only changes whether
     it is visible without first activating the button. */
  .site-head[data-menu-open="true"] .pa-nav,
  .site-head .pa-nav[data-open="true"] {
    display: flex;
  }
  .site-head .pa-nav {
    display: none;
    order: 3;
    width: 100%;
    margin-inline-start: 0;
    justify-content: flex-start;
    padding-top: var(--pa-s3);
    border-top: 1px solid var(--pa-hair);
  }
}

.site-foot { border-top: 1px solid var(--pa-hair); padding-block: var(--pa-s8) var(--pa-s6); }
.site-foot__grid {
  display: grid;
  grid-template-columns: minmax(200px, 1.3fr) repeat(4, minmax(140px, 1fr));
  gap: var(--pa-s6);
}
.site-foot__brand .vl-lockup { margin-bottom: var(--pa-s3); }
.site-foot__group h2 {
  font-family: var(--pa-font-mono);
  font-size: var(--pa-step--1);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pa-text-muted);
  margin: 0 0 var(--pa-s3);
}
.site-foot__group ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--pa-s2); }
.site-foot__group a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  color: var(--pa-text-muted);
  text-decoration: none;
}
.site-foot__group a:hover { color: var(--pa-text); text-decoration: underline; }

.signoff {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pa-s3);
  justify-content: space-between;
  margin-top: var(--pa-s7);
  padding-top: var(--pa-s5);
  border-top: 1px solid var(--pa-hair);
  color: var(--pa-text-muted);
  font-size: var(--pa-step--1);
}

@media (max-width: 780px) {
  .site-foot__grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
  .site-foot__grid { grid-template-columns: 1fr; }
}
