/* ====================================================================
   www.nxos.com
   ====================================================================

   INFLUENCES, AND WHAT WAS TAKEN FROM EACH.

   Three reference systems were studied. What follows borrows their
   REASONING, not their palettes — importing another product's accent
   colour would break continuity with the NXOS product pages a visitor
   lands on next.

     Linear — elevation by hairline border rather than shadow, and
     monospace reserved strictly for technical metadata. NXOS is full
     of things that want that register: serials, registry IDs, edition
     counters, tap counts.

     Origin Financial — three typographic voices with distinct jobs
     (light display for statements, sans for UI, mono for data), and
     chromatic colour appearing ONLY as full-bleed category panels.
     That maps exactly onto the three-audience section.

     Henry — one hero moment carrying the page, colour rationed to
     functional punctuation, and the light-card-on-dark inversion for
     product proof.

   THE PALETTE IS NXOS'S OWN, copied from app.css deliberately rather
   than imported. If the brand moves, move it here too; the list is
   short and it is the only thing needing sync.
   ==================================================================== */

:root {
  /* --- brand, from app.css --- */
  --ink:        #f4f3ef;
  --paper:      #0b0b0c;
  --line:       #26262a;
  --accent:     #ff4f00;   /* market and value ONLY */
  --qf:         #2800ff;   /* QF-TAG authentication ONLY */
  --verified:   #4ade80;

  /* --- surfaces, stepped rather than shadowed --- */
  --paper-1:    #111114;
  --paper-2:    #17171b;
  --paper-3:    #1d1d22;

  /* --- text, never pure white below display sizes --- */
  --ink-soft:   #a3a3a8;
  --ink-faint:  #6b6b72;
  --ink-ghost:  #45454b;

  /* --- three voices --- */
  --font-display: ui-serif, Charter, "Bitstream Charter", "Iowan Old Style",
                  Georgia, "Times New Roman", serif;
  --font-ui: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI",
             Inter, "Helvetica Neue", Arial, sans-serif;
  --font-data: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

  /*
    Display sizes run large and LIGHT. The instinct at 96px is to reach
    for 700; every reference studied does the opposite, and they are
    right — weight at that scale reads as shouting, where restraint
    reads as assurance.
  */
  --t-hero:   clamp(3rem, 9vw, 8.5rem);
  --t-h2:     clamp(2rem, 4.5vw, 4rem);
  --t-h3:     clamp(1.35rem, 2.2vw, 2rem);
  --t-lede:   clamp(1.0625rem, 1.4vw, 1.375rem);
  --t-body:   1.0625rem;
  --t-small:  0.9375rem;
  --t-label:  0.75rem;

  --sp-1: .25rem;  --sp-2: .5rem;   --sp-3: .75rem;  --sp-4: 1rem;
  --sp-6: 1.5rem;  --sp-8: 2rem;    --sp-12: 3rem;   --sp-16: 4rem;
  --sp-24: 6rem;   --sp-32: 8rem;   --sp-40: 10rem;

  --gutter: clamp(1.25rem, 5vw, 4.5rem);
  --measure: 60ch;
  --frame: 1240px;

  /* Motion. Short states are quick and predictable; reveals are long
     and eased. No springs, no overshoot — the product is a registry. */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 160ms;
  --dur-slow: 900ms;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  background: var(--paper);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--t-body);
  line-height: 1.55;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg, canvas { display: block; max-width: 100%; }
a { color: inherit; }

h1, h2, h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 300;
  letter-spacing: -0.028em;
  line-height: 1.02;
  text-wrap: balance;
}
p { margin: 0; text-wrap: pretty; }

.wrap { max-width: var(--frame); margin-inline: auto; padding-inline: var(--gutter); }
.measure { max-width: var(--measure); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--ink); color: var(--paper);
  padding: var(--sp-3) var(--sp-4); font-size: var(--t-small);
}
.skip:focus { left: var(--sp-4); top: var(--sp-4); }

:where(a, button):focus-visible {
  outline: 2px solid var(--qf);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---- data labels: the third voice ---------------------------------- */
.label {
  font-family: var(--font-data);
  font-size: var(--t-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 400;
}
.label--qf { color: var(--qf); }
.label--accent { color: var(--accent); }

/* ---- nav ------------------------------------------------------------ */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  display: flex; align-items: center; gap: var(--sp-8);
  padding: var(--sp-4) var(--gutter);
  /*
    Transparent at rest so the hero reads full-bleed; the surface
    fades in only once you have scrolled past it. `.is-stuck` is set
    by script — a permanently frosted bar over a 3D hero looks like a
    smudge on the glass.
  */
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              backdrop-filter var(--dur-fast) var(--ease);
}
.nav.is-stuck {
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  backdrop-filter: blur(14px) saturate(1.2);
  border-bottom-color: var(--line);
}
.nav-mark { display: flex; align-items: center; gap: var(--sp-3); text-decoration: none; }
.nav-mark svg { width: 32px; height: auto; color: var(--ink); }
.nav-mark span {
  font-family: var(--font-data); font-size: var(--t-label);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-soft);
}
.nav-links { display: none; gap: var(--sp-6); margin-left: auto; }
.nav-links a {
  text-decoration: none; font-size: var(--t-small); color: var(--ink-soft);
  transition: color var(--dur-fast) var(--ease);
}
.nav-links a:hover { color: var(--ink); }
.nav-cta { margin-left: auto; }
@media (min-width: 58rem) {
  .nav-links { display: flex; }
  .nav-cta { margin-left: 0; }
}

/* ---- buttons -------------------------------------------------------- */
/*
  Pill geometry, and exactly one filled button per view. The filled
  treatment is ink-on-paper rather than a brand colour: --accent means
  market and value everywhere else in the product, and spending it on
  a nav button would dilute the one signal a shopper scans for.
*/
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp-2);
  padding: 0.7rem 1.35rem;
  font: inherit; font-size: var(--t-small); font-weight: 500;
  letter-spacing: -0.01em;
  text-decoration: none; white-space: nowrap;
  border: 1px solid var(--ink); border-radius: 100px;
  background: var(--ink); color: var(--paper);
  cursor: pointer;
  transition: opacity var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn:hover { opacity: 0.86; }
.btn:active { transform: translateY(1px); }
.btn-ghost {
  background: transparent; color: var(--ink); border-color: var(--ink-ghost);
}
.btn-ghost:hover { border-color: var(--ink); opacity: 1; }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); }

.btn .arw { transition: transform var(--dur-fast) var(--ease); }
.btn:hover .arw { transform: translateX(3px); }

/* ---- scroll reveal -------------------------------------------------- */
/*
  Long, eased, and ONCE. Elements that re-animate every time they
  re-enter the viewport turn a scroll-back into a distraction.

  The reveal is applied by script adding .is-in; without JS nothing is
  hidden, so the page reads perfectly with the script blocked. That
  ordering matters — the common version of this hides content in CSS
  and reveals it in JS, which turns a script failure into a blank page.
*/
/*
  SCOPED TO html.js — this is the load-bearing part.

  The obvious version writes `.rise { opacity: 0 }` and reveals it from
  script. That hides the entire page behind a successful script load:
  block the JS, or let it throw before the observer is wired, and a
  visitor gets a blank document with the content technically present
  and completely invisible.

  The `js` class is set by a one-line inline script in <head>, before
  first paint. So the hidden state can only ever exist in a browser
  that is already running our JavaScript — and the no-JS rendering is
  simply the finished page, unanimated.
*/
html.js .rise { opacity: 0; transform: translateY(18px); }
html.js .rise.is-in {
  opacity: 1; transform: none;
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}
.rise-2 { transition-delay: 80ms; }
.rise-3 { transition-delay: 160ms; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  html.js .rise, html.js .rise.is-in { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ==================================================================== *
 * HERO
 * ==================================================================== */

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-items: end;
  padding: calc(var(--sp-32) + 4rem) 0 var(--sp-16);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}

/*
  The canvas sits BEHIND everything and is aria-hidden. It carries no
  information the copy does not also carry — a visitor with WebGL
  disabled loses atmosphere, never meaning.
*/
.hero-canvas {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  opacity: 0;
  transition: opacity 1.6s var(--ease-out);
}
.hero-canvas.is-ready { opacity: 1; }

/* The static stand-in. Painted immediately, and removed only once the
   WebGL scene has actually rendered a frame — not merely once the
   script has loaded. */
.hero-fallback {
  position: absolute; inset: 0; z-index: 0;
  display: grid; place-items: center;
  opacity: 1;
  transition: opacity 1.2s var(--ease-out);
}
.hero-fallback.is-hidden { opacity: 0; }
.hero-fallback svg { width: min(78vw, 900px); height: auto; }

.hero-scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(120% 90% at 50% 0%, transparent 30%, var(--paper) 100%),
    linear-gradient(to top,
      var(--paper) 0%,
      color-mix(in srgb, var(--paper) 72%, transparent) 38%,
      transparent 72%);
}

.hero-body { position: relative; z-index: 2; }
.hero h1 { font-size: var(--t-hero); max-width: 16ch; }
.hero h1 em {
  font-style: italic;
  color: var(--ink);
}
.hero-lede {
  margin-top: var(--sp-6);
  font-size: var(--t-lede);
  color: var(--ink-soft);
  max-width: 52ch;
  font-weight: 300;
}
.hero-cta { margin-top: var(--sp-8); }

/* The live counter under the hero — the chain's own numbers, in the
   data voice. */
.hero-meta {
  margin-top: var(--sp-12);
  display: flex; flex-wrap: wrap; gap: var(--sp-8);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--line);
}
.hero-meta div { display: grid; gap: 2px; }
.hero-meta .v {
  font-family: var(--font-data);
  font-size: 1.125rem;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* ==================================================================== *
 * SECTIONS
 * ==================================================================== */

.section { padding-block: clamp(var(--sp-24), 14vw, var(--sp-40)); }
.section + .section { border-top: 1px solid var(--line); }

.section-head { max-width: 44ch; }
.section-head h2 { font-size: var(--t-h2); margin-top: var(--sp-6); }
.section-head p {
  margin-top: var(--sp-6); font-size: var(--t-lede);
  color: var(--ink-soft); font-weight: 300;
}

.statement {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t-h2);
  line-height: 1.1;
  letter-spacing: -0.028em;
  max-width: 22ch;
}
.statement em { font-style: italic; color: var(--accent); }

/* ---- the problem: three hairline cells ----------------------------- */
.cells { display: grid; gap: 1px; margin-top: var(--sp-16); background: var(--line); }
@media (min-width: 54rem) { .cells { grid-template-columns: repeat(3, 1fr); } }
.cell { background: var(--paper); padding: var(--sp-8) var(--sp-6); }
.cell h3 { font-size: var(--t-h3); margin: var(--sp-4) 0 var(--sp-3); }
.cell p { color: var(--ink-soft); font-size: var(--t-small); }

/* ---- how it works: numbered steps ---------------------------------- */
.steps { display: grid; gap: 1px; margin-top: var(--sp-16); background: var(--line); }
@media (min-width: 54rem) { .steps { grid-template-columns: repeat(4, 1fr); } }
.step {
  background: var(--paper); padding: var(--sp-8) var(--sp-6);
  position: relative;
  transition: background var(--dur-fast) var(--ease);
}
.step:hover { background: var(--paper-1); }
.step-n { font-family: var(--font-data); font-size: var(--t-label); color: var(--qf); }
.step h3 { font-size: var(--t-h3); margin: var(--sp-6) 0 var(--sp-3); }
.step p { color: var(--ink-soft); font-size: var(--t-small); }

/* ==================================================================== *
 * THE THREE AUDIENCES — the only chromatic surfaces on the page
 *
 * Colour is spent here and nowhere else. Each panel is a full-bleed
 * tile whose hue IS its identity, which is why they carry no icons:
 * the colour does the categorising.
 *
 * The hues are derived from the NXOS tokens rather than invented —
 * QF blue for the rights holder (issuance, verification), market
 * orange for the brand (selling, value), and a near-white inversion
 * for the collector, because the collector's surface in the product
 * is the one that shows a real page.
 * ==================================================================== */

.aud { display: grid; gap: var(--sp-4); margin-top: var(--sp-16); }
@media (min-width: 62rem) { .aud { grid-template-columns: repeat(3, 1fr); } }

.tile {
  position: relative;
  border-radius: 24px;
  padding: clamp(var(--sp-8), 3vw, var(--sp-12));
  display: flex; flex-direction: column; gap: var(--sp-4);
  min-height: 30rem;
  overflow: hidden;
  transition: transform var(--dur-slow) var(--ease-out);
}
@media (hover: hover) { .tile:hover { transform: translateY(-4px); } }

.tile h3 {
  font-size: clamp(1.75rem, 2.4vw, 2.4rem);
  margin-bottom: var(--sp-2);
}
.tile > p { font-size: var(--t-small); }
.tile ul { list-style: none; margin: var(--sp-4) 0 0; padding: 0; display: grid; gap: var(--sp-3); }
.tile li {
  display: grid; grid-template-columns: auto 1fr; gap: var(--sp-3);
  font-size: var(--t-small);
}
.tile li::before { content: "—"; font-family: var(--font-data); opacity: 0.5; }
.tile .tile-cta { margin-top: auto; padding-top: var(--sp-6); }

.tile--ip     { background: var(--qf); color: #f2f1ff; }
.tile--ip .label, .tile--ip > p, .tile--ip li { color: #c9c6ff; }

/* The brand orange, unmodified, carrying white type.
   A DELIBERATE, OWNER-APPROVED EXCEPTION TO AA. White on #ff4f00 is
   3.30:1, under the 4.5:1 the other two tiles hold. Deepening the
   orange to fix that was tried and rejected — this hue is the brand.
   So: pure white only (any tint drops it further), no opacity on the
   body copy, and hierarchy from size and weight instead. The contrast
   test enforces 3:1 here and 4.5:1 elsewhere, so a regression past
   even this floor still fails the suite. */
.tile--brand  { background: var(--accent); color: #fff; }
.tile--brand .label, .tile--brand > p, .tile--brand li { color: #fff; }
.tile--brand .label { opacity: 0.85; }   /* uppercase mono, tracked, decorative */

/* The inversion. One light surface on an otherwise dark page, used
   for the audience whose product surface is a real, readable page. */
.tile--collector { background: #e8e6e0; color: #16161a; }
.tile--collector .label, .tile--collector > p, .tile--collector li { color: #55555c; }

.tile .btn { border-color: currentColor; background: currentColor; }
.tile--ip .btn        { color: #f2f1ff; }
.tile--ip .btn span   { color: var(--qf); }
/* White pill. The label inside it cannot be --accent: #ff4f00 on white
   is 3.3:1, which is a heading ratio, not a button-label one. #b32f00
   is the same hue at 6.3:1 on white, and it is a 12px control sitting
   on orange — the pill does the signalling, the text just has to be
   readable. Confined to this one rule. */
.tile--brand .btn     { color: #fff; }
.tile--brand .btn span{ color: #b32f00; }
.tile--collector .btn { color: #16161a; }
.tile--collector .btn span { color: #e8e6e0; }

/* ==================================================================== *
 * PROOF — the light card on dark
 * ==================================================================== */

.split { display: grid; gap: var(--sp-12); align-items: center; }
@media (min-width: 58rem) { .split { grid-template-columns: 1fr 1fr; gap: var(--sp-16); } }

/* ---- the logo film, beside How it works ----------------------------- */
/*
  Screen blend dissolves the render's black background into the page.
  The video is decorative: aria-hidden and out of the tab order in the
  markup, and it carries no information the copy beside it does not.
*/
.logo-film { margin: 0; }
.logo-film video {
  width: 100%; height: auto;
  max-width: 30rem;
  margin-inline: auto;
  mix-blend-mode: screen;
  border-radius: 8px;
}
@media (min-width: 58rem) {
  .split--how { grid-template-columns: 1.1fr 0.9fr; align-items: center; }
}
/* Below the split, the film sits under the copy and does not need to be
   as large as a phone screen is wide. */
@media (max-width: 57.99rem) {
  .logo-film video { max-width: 20rem; }
}

/*
  The wedge runs copy left, verification card right.

  The ratio is what sizes the card, NOT its max-width. At the 1240px
  frame the content box is about 1096px; minus the sp-16 gap that
  leaves ~1032px to divide, so at 1.05/0.95 the right column came out
  around 490px — under the card's own 34rem max-width, which therefore
  never applied. Raising the max-width alone would have changed
  nothing. Giving the column more of the split is the change; the
  max-width is now the ceiling it grows into.

  The prose column still needs enough measure to hold the h2 at its
  display size, so this stays close to even rather than lopsided.
*/
@media (min-width: 58rem) {
  .split--wedge { grid-template-columns: 1fr 1.08fr; align-items: start; }
}

/* ---- the QF-TAG object ---------------------------------------------- */
/*
  The only rendered object on a page otherwise made of type and
  hairlines, which is exactly why it lands.

  It carries chromatic weight without breaking the colour rationing,
  because it introduces no new hue — the two discs are the brand's own
  near-black and QF-TAG blue.

  The glow is a radial wash BEHIND the image rather than a filter on
  it. A drop-shadow filter on a transparent PNG traces the alpha edge
  of every raised dot, which reads as a halo rather than as light.
*/
.coin {
  margin: 0;
  position: relative;
  display: grid;
  place-items: center;
}
.coin::before {
  content: "";
  position: absolute;
  inset: 8%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    color-mix(in srgb, var(--qf) 26%, transparent) 0%,
    transparent 68%);
  filter: blur(38px);
  pointer-events: none;
}
.coin img {
  position: relative;
  width: 100%;
  max-width: 30rem;
  height: auto;
}

/*
  A slow drift, not a spin. The object is meant to read as a physical
  thing catching light, and anything faster turns a product shot into
  a loading indicator.

  Suppressed under prefers-reduced-motion by the global rule further
  up, which zeroes animation-duration everywhere.
*/
@media (prefers-reduced-motion: no-preference) {
  .coin img { animation: coin-drift 9s var(--ease) infinite; }
}
@keyframes coin-drift {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-14px) rotate(-1.2deg); }
}

/*
  A real verification page, rendered as a light card. The inversion is
  the point: it reads as a screenshot rather than as more chrome, and
  it is the only place on the page a visitor sees what the product
  actually looks like.

  Centred and narrow now that it owns a row rather than a column —
  a data table stretched to full page width stops being readable as a
  screenshot and starts reading as a layout.
*/
/* Stacked, the grid's own gap does the spacing; the sp-16 below dates
   from when this card sat on its own row under the coin. */
.split--wedge .proof-card { margin-top: 0; }

/*
  ALIGNED TO THE HEADLINE, NOT TO THE COLUMN.

  align-items:start lines the card up with the top of the left column —
  but that column starts with a tracked-out 12px eyebrow, and the eye
  anchors on "NXOS doesn't", not on the label. Aligning to the column
  is geometrically correct and looks wrong, which is why the card read
  as floating high.

  The offset is derived rather than measured off a screenshot: it is
  the eyebrow's line box plus the h2's own top margin, so it stays
  right if either token changes. 1.55 is the body line-height the label
  inherits.
*/
@media (min-width: 58rem) {
  .split--wedge .proof-card {
    margin-top: calc(var(--t-label) * 1.55 + var(--sp-6));
  }
}

.proof-card {
  max-width: 38rem;
  margin-inline: auto;
  margin-top: var(--sp-16);
  background: #f4f3ef; color: #16161a;
  border-radius: 14px;
  padding: var(--sp-8);
  font-family: var(--font-ui);
  box-shadow: 0 40px 80px -40px rgba(0,0,0,0.8);
}
.proof-card .verdict {
  display: flex; align-items: center; gap: var(--sp-3);
  font-family: var(--font-data); font-size: var(--t-label);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: #0a7d32;
}
.proof-card .dot {
  width: 8px; height: 8px; border-radius: 50%; background: #0a7d32;
}
/* A wider box with the same type inside just reads as more padding, so
   the interior scales with it: title up, rows to full body size, and
   taller rows so it reads as a real data table rather than a caption. */
.proof-card h3 {
  font-family: var(--font-ui); font-weight: 600; letter-spacing: -0.02em;
  font-size: 1.6rem; margin: var(--sp-4) 0 var(--sp-6);
}
.proof-rows { display: grid; gap: 0; border-top: 1px solid #dcdad4; }
.proof-rows div {
  display: flex; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-4) 0; border-bottom: 1px solid #dcdad4;
  font-size: var(--t-body);
}
.proof-rows dt, .proof-rows .k { color: #6b6b72; }
/*
  The product code is the longest value on the card and it just grew.
  Monospace at body size can push it into the label; letting it wrap
  keeps the row intact instead of overflowing the box.
*/
.proof-rows .v {
  font-family: var(--font-data); color: #16161a;
  text-align: right; overflow-wrap: anywhere;
}
.proof-note {
  margin-top: var(--sp-6); font-size: var(--t-small);
  color: #6b6b72; line-height: 1.6;
}

/* ==================================================================== *
 * THE ACCESS FORM
 * ==================================================================== */

.access-form {
  background: var(--paper-1);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: clamp(var(--sp-6), 3vw, var(--sp-8));
  display: grid;
  gap: var(--sp-4);
}

.field { display: grid; gap: var(--sp-2); }
.field label {
  font-family: var(--font-data);
  font-size: var(--t-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.field .opt { text-transform: none; letter-spacing: 0; opacity: 0.7; }

.field input,
.field select,
.field textarea {
  font: inherit;
  font-size: var(--t-small);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.7rem 0.85rem;
  width: 100%;
  transition: border-color var(--dur-fast) var(--ease);
}
.field textarea { resize: vertical; min-height: 4.5rem; }
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--qf);
}
.field input:user-invalid { border-color: var(--accent); }

.field-row { display: grid; gap: var(--sp-4); }
@media (min-width: 30rem) { .field-row { grid-template-columns: 1fr 1fr; } }

/*
  THE HONEYPOT.

  Not display:none and not type="hidden" — plenty of bots skip both,
  which is the entire point of the trap. Positioned off-canvas so a
  person never encounters it, with tabindex and aria-hidden on the
  markup keeping it out of the keyboard order and the accessibility
  tree.
*/
.hp {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.form-msg {
  font-size: var(--t-small);
  min-height: 1.4em;
  color: var(--ink-soft);
}
.form-msg[data-state="error"] { color: var(--accent); }
.form-msg[data-state="ok"] { color: var(--verified); }

.form-fine {
  font-size: var(--t-label);
  color: var(--ink-faint);
  line-height: 1.6;
}

.access-form .btn:disabled { opacity: 0.5; cursor: default; }

/* ==================================================================== *
 * HONESTY
 * ==================================================================== */

.honest {
  background: var(--paper-1);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: clamp(var(--sp-8), 4vw, var(--sp-16));
}
.honest h2 { font-size: var(--t-h3); margin: var(--sp-6) 0 var(--sp-6); }
.honest p { color: var(--ink-soft); max-width: var(--measure); font-size: var(--t-small); }
.honest p + p { margin-top: var(--sp-4); }

/* ==================================================================== *
 * CLOSE + FOOTER
 * ==================================================================== */

.close-grid { display: grid; gap: 1px; margin-top: var(--sp-16); background: var(--line); }
@media (min-width: 54rem) { .close-grid { grid-template-columns: repeat(3, 1fr); } }
.close-cell {
  background: var(--paper); padding: var(--sp-8) var(--sp-6);
  display: flex; flex-direction: column; gap: var(--sp-4);
}
.close-cell h3 { font-size: var(--t-h3); }
.close-cell p { color: var(--ink-soft); font-size: var(--t-small); flex: 1; }

.foot {
  border-top: 1px solid var(--line);
  padding: var(--sp-16) var(--gutter) var(--sp-12);
  max-width: var(--frame); margin-inline: auto;
  display: grid; gap: var(--sp-8);
}
@media (min-width: 54rem) { .foot { grid-template-columns: 2fr 1fr 1fr; } }
.foot-mark svg { width: 42px; color: var(--ink); }
.foot h4 {
  font-family: var(--font-data); font-size: var(--t-label);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-faint); margin: 0 0 var(--sp-4); font-weight: 400;
}
.foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }
.foot a { color: var(--ink-soft); text-decoration: none; font-size: var(--t-small); }
.foot a:hover { color: var(--ink); }
.foot-fine {
  grid-column: 1 / -1; border-top: 1px solid var(--line);
  padding-top: var(--sp-6); color: var(--ink-faint); font-size: var(--t-label);
  line-height: 1.7;
}
