/* =====================================================================
   NEXORA WEB — style.css
   Agency Portfolio Site · handwritten, no frameworks
   Colors are ONLY ever referenced through CSS custom properties.
   ===================================================================== */

/* ---------------------------------------------------------------------
   0. Self-hosted font (Inter variable) — no external dependency
   --------------------------------------------------------------------- */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/Inter.woff2') format('woff2');
}

/* ---------------------------------------------------------------------
   1. Design Tokens
   --------------------------------------------------------------------- */
/* VISUAL MIGRATION — this block is now a compatibility layer.
   The homepage and the hero used to be two design systems in one stylesheet.
   These legacy names are kept because ~150 declarations reference them, but
   their *values* now alias the --nx-* tokens defined further down (§ "NEXORA
   HERO"). There is one source of truth; this block only maps old names onto it.

   Forward references resolve correctly: custom properties are substituted at
   computed-value time, not at parse time, so a token declared later in the
   same :root cascade is available here. Verified in-browser before relying on it.

   Retire this block once every selector has been moved onto --nx-* directly. */
:root {
  /* Colors — field */
  --bg-primary:       var(--nx-field);
  --bg-secondary:     var(--nx-field-raised);
  --bg-deep:          var(--nx-field-deep);
  /* Surfaces — parchment alpha over obsidian, never violet. Every card on the
     page (.service-card, .price-card, .work-card, .care-card, .addons,
     .pricing-toggle, .service-card__icon) resolves through these two. */
  --bg-card:          var(--nx-surface);
  --bg-card-hover:    var(--nx-surface-hover);


  /* Gold accent system.
     #C4A35A is brand-locked: it is the wedge in the logo mark and in brand/print.
     Contrast on --bg-primary: --gold 7.5:1 · --gold-light 9.4:1 · --gold-muted 3.6:1.
     --gold-muted clears 3:1 for dots/rules/icon strokes but is NOT legible as
     text — don't use it for copy. --gold-deep is a gradient end-stop only; it
     is the darkest thing --bg-primary text ever lands on (5.2:1 on the badge). */
  --gold:             #C4A35A;
  --gold-light:       #D4B97A;
  --gold-deep:        #A8843F;

  --gold-muted:       rgba(196, 163, 90, 0.62);
  --gold-soft:        rgba(196, 163, 90, 0.10);
  --gold-subtle:      rgba(196, 163, 90, 0.15);
  --gold-strong:      rgba(196, 163, 90, 0.25);

  --gold-border:       rgba(196, 163, 90, 0.28);
  --gold-border-hover: rgba(196, 163, 90, 0.50);
  --gold-glow:         rgba(196, 163, 90, 0.22);
  --gold-glow-soft:    rgba(196, 163, 90, 0.10);

  /* Ambient background warmth — the core of the large section washes in §19.
     Weaker than every other gold token: it is the only one that ever sits
     behind body copy, so it must not move contrast. Every wash core is
     positioned over empty space; measured alpha under body text stays ~0.03.
     --gold-ambient-strong is used only where a wash has no text beneath it
     (the final-CTA top-light). Both are reduced on phones (override in §19). */
  --gold-ambient:        rgba(196, 163, 90, 0.14);
  --gold-ambient-strong: rgba(196, 163, 90, 0.18);

  /* Type — warm parchment, matching the hero. The cool #F0EEFF survives only
     in the navigation lockup (.logo-shimmer, --nx-logo-white), which is
     deliberate: that is the wordmark's own colour, not body text. */
  --text-primary:     var(--nx-parchment);
  --text-secondary:   var(--nx-parchment-dim);
  --text-muted:       var(--nx-parchment-faint);

  /* Hairlines — the hero's own edge language. 17 declarations resolve here:
     every card, the FAQ rows, the add-ons panel, the pricing toggle and the
     footer rule. Neutral, not violet: violet is atmosphere and takes no edge. */
  --border-subtle:    var(--nx-line);
  --border-gold:      var(--gold-border);

  --nav-bg:           rgba(var(--nx-obsidian-rgb), 0.82);

  /* RGB triplets — compose custom alphas through variables (never raw hex) */
  --gold-rgb:           196, 163, 90;
  --text-primary-rgb:   240, 238, 255;

  /* Forms */
  /* Form surfaces sit one step above a card so a field reads as enterable
     rather than as another panel. The focus ring is gold because gold is the
     interaction accent everywhere else in this system. */
  --input-bg:       var(--nx-surface-hover);
  --input-border:   var(--nx-line);
  --focus-ring:     var(--gold-glow);
  --error:          #F87171;
  --error-bg:       rgba(248, 113, 113, 0.08);
  --error-border:   rgba(248, 113, 113, 0.2);
  --success:        #86EFAC;
  --success-bg:     rgba(34, 197, 94, 0.08);
  --success-border: rgba(34, 197, 94, 0.25);

  /* Browser frame */
  --browser-chrome:   var(--nx-charcoal);
  --browser-url-bg:   rgba(var(--nx-parchment-rgb), 0.05);
  --dot-red:          #FF5F57;
  --dot-yellow:       #FFBD2E;
  --dot-green:        #28C840;

  --gradient-text:    linear-gradient(90deg, var(--nx-parchment), var(--gold));
  --gradient-gold:    linear-gradient(135deg, var(--gold-light) 0%, var(--gold) 55%, var(--gold-deep) 100%);
  --gradient-bg:      linear-gradient(135deg,
                        var(--nx-field) 0%,
                        var(--nx-field-deep) 60%,
                        var(--nx-charcoal) 100%);

  /* Thin warm separator — fades in from both ends */
  --gold-hairline:    linear-gradient(90deg, transparent, var(--gold-border-hover), transparent);

  /* Typography */
  --font-sans:        'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-size-xs:     11px;
  --font-size-sm:     13px;
  --font-size-base:   15px;
  --font-size-lg:     18px;
  --font-size-xl:     24px;
  --font-size-2xl:    32px;
  --font-size-3xl:    44px;
  --font-size-4xl:    56px;

  /* Spacing */
  --space-xs:   8px;
  --space-sm:   16px;
  --space-md:   24px;
  --space-lg:   48px;
  --space-xl:   80px;
  --space-2xl:  120px;

  /* Layout — hybrid rail.
     The page has ONE outer rail, shared with the hero, so navigation, hero and
     every section start on the same vertical line. Content is then capped
     INSIDE that rail per section, so aligning the page does not stretch it.

     Before this, the hero ran on --maxw/--gut while sections ran on a centred
     1200px box, and the offset between the two swung 151px across the range
     and changed sign near 1280 (hero 132px left of the sections at 1920, 19px
     right of them at 1024). That inconsistency is most of what read as "two
     systems".

     --container-max/-pad are kept as names because 30+ rules reference them;
     they now resolve to the hero's rail. */
  --container-max:  var(--maxw);   /* 1560px */
  --container-pad:  var(--gut);    /* clamp(1.25rem, 4.2vw, 4.5rem) */

  /* Internal caps. Each is a maximum, not a width — below these viewports the
     content is fluid exactly as before. Left-aligned to the rail so every
     block shares one left edge; only the two deliberately centred
     compositions (Pricing, final CTA) centre within it. */
  /* 64rem, not 62: the longest EN heading ("Stop losing clients to competitors
     with worse products.") measures 1001px at its 40px desktop size, and a
     992px cap orphaned "products." onto its own line by 9px. Every other
     heading sits between 545 and 869px, so this is headroom, not a stretch. */
  --cap-text:       64rem;    /* headings + subs — display measure          */
  --cap-grid:       1320px;   /* Services, Work feature                     */
  --cap-process:    1200px;   /* five steps stay tight                      */
  --cap-pricing:    1360px;   /* four cards; wider and they lose density    */
  --cap-narrow:     1040px;   /* FAQ, care plans, add-ons                   */
  /* Downstream surfaces are 2px. The hero stays 0px — it sets no radius at
     all, so it is unaffected by these tokens and needs no exception.

     Why 2px and not 0: at 1440 and above the two are visually identical (a 2px
     corner on a 368px card is half a percent of the edge), so 0 buys no extra
     rigour where most of the page is read. At 1:1 on a phone the difference is
     real, and 0 tips form fields from editorial into brittle. 2px keeps the
     square silhouette and stops the corner reading as cut.

     Two tiers by intent: the hero's editorial surfaces are sharp, downstream
     interactive surfaces are fractionally eased. Semantic tags stay pills
     (999px, set locally) because a pill reads as "tag", not as "surface":
     .price-card__badge and .work-card__tag. Status dots and the browser
     traffic lights stay circles. */
  --radius-sm:      2px;
  --radius-md:      2px;
  --radius-lg:      2px;
  --radius-xl:      2px;

  /* Z-index scale */
  --z-base:     1;
  --z-raised:   10;
  --z-nav:      100;
  --z-overlay:  200;

  /* Transitions */
  --transition-fast:    0.15s ease;
  --transition-base:    0.25s ease;
  --transition-slow:    0.4s ease;
}

/* ---------------------------------------------------------------------
   2. Reset & Base
   --------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  /* Offset anchored sections so they don't hide under the fixed nav */
  scroll-padding-top: 90px;
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font-sans);
  background: var(--bg-primary);
  color: var(--text-primary);
  font-size: var(--font-size-base);
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-osgrayscale: grayscale;
  text-rendering: optimizeLegibility;
}

img, svg { display: block; max-width: 100%; }

a { color: inherit; }

button { font-family: inherit; }

/* Ensure the [hidden] attribute always wins over display rules (pricing toggle) */
[hidden] { display: none !important; }

/* Visible focus rings for keyboard users (a11y) */
:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

/* Skip link for keyboard / screen-reader users */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: var(--z-overlay);
  background: var(--gold);
  color: var(--nx-ink);
  padding: 10px 18px;
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
  font-weight: 600;
}
.skip-link:focus { left: var(--space-sm); top: var(--space-sm); }

/* ---------------------------------------------------------------------
   3. Utilities
   --------------------------------------------------------------------- */
.text-gradient {
  background: var(--gradient-text);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  color: transparent;
}

.container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
}

.section {
  padding: var(--space-2xl) 0;
  position: relative;
}

/* Matches .nx-eyebrow: same weight, same track. The hero's wide tracking is
   what makes an eyebrow read as a system label rather than small bold text,
   and it was the most obvious typographic tell between the two halves. */
.section-label {
  font-size: var(--font-size-xs);
  font-weight: 400;
  letter-spacing: var(--nx-label-track);
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: var(--space-sm);
  display: inline-block;
}

/* Eyebrow rule. Scoped to <span> so the <a class="section-label"> back-link
   on work.html doesn't get a leading dash. */
span.section-label::before {
  content: "";
  display: inline-block;
  vertical-align: middle;
  position: relative;
  top: -1px;
  width: 20px;
  height: 1px;
  margin-right: 10px;
  background: linear-gradient(90deg, transparent, var(--gold));
}

/* Fluid, following the hero's philosophy rather than its numbers, and sized so
   the hero title is larger at every single width.

   That last part is not automatic. The shipped design had section headings at a
   flat 44px while the hero at 1280 is 42.75px — so they were already fractionally
   *larger* than the hero through the middle of the range. A first attempt at
   clamp(2rem, 0.9rem + 2.4vw, 2.75rem) reproduced that inversion by hitting its
   44px cap at 1230px, well before the hero reaches its own.

   2.9vw with a 2.5rem cap keeps a clear gap the whole way:
     320   30 vs 32     768   30 vs 49     1280  37 vs 43
     1024  30 vs 37     1440  40 vs 48     1920  40 vs 56
   Desktop headings come down 44 -> 40, which is what buys the hierarchy.
   Weight 600 -> 500 and tracking -0.01 -> -0.022em are the hero's. */
.section-heading {
  font-size: clamp(1.85rem, 2.9vw, 2.5rem);
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.14;
  margin-bottom: var(--space-sm);
  letter-spacing: -0.022em;
}

.section-sub {
  font-size: var(--font-size-lg);
  color: var(--text-secondary);
  max-width: 560px;
  line-height: 1.65;
}

.section-head { margin-bottom: var(--space-xl); max-width: var(--cap-text); }

/* ---- Internal content caps (see --cap-* in §1) --------------------------
   The rail aligns; these stop it stretching. Left-aligned, so the section
   heading, its sub and the grid beneath all share one left edge on the rail. */
.services-grid    { max-width: var(--cap-grid); }
.process-steps    { max-width: var(--cap-process); }
.portfolio-feature,
.portfolio-grid   { max-width: var(--cap-grid); }
.faq-grid,
.care__grid,
.addons           { max-width: var(--cap-narrow); }

/* The two centred compositions centre inside the rail instead. Pricing's head
   is already centre-aligned in the markup, so its grid must follow or the
   cards would sit left of their own heading. */
.pricing-grid     { max-width: var(--cap-pricing); margin-inline: auto; }
#pricing .section-head { max-width: var(--cap-text); margin-inline: auto; }

/* Atmosphere layer. Was a hard-edged circle softened by filter: blur(80px) —
   a real-time GPU blur on a 460px element, and the one technique the hero
   system explicitly excludes (its whole field is built from gradients, no blur
   filters, no feTurbulence). A radial-gradient produces the same falloff with
   no filter, no compositing cost and no blur-edge banding on a dark field. */
.glow-orb {
  position: absolute;
  pointer-events: none;
  z-index: var(--z-base);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}

/* ---------------------------------------------------------------------
   4. Navigation
   --------------------------------------------------------------------- */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-nav);
  padding: 20px 0;
  transition: background var(--transition-base), padding var(--transition-base);
}

.nav.scrolled {
  background: var(--nav-bg);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  padding: 14px 0;
  border-bottom: 1px solid var(--gold-soft);
}

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
}

.nav__logo {
  font-size: 20px;
  font-weight: 600;
  color: var(--text-primary);
  text-decoration: none;
  letter-spacing: 0.02em;
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.nav__logo-dot { color: var(--gold); }

/* ── Logo lockup (mark + shimmer wordmark) ──────────────────────────── */
.logo-mark {
  height: 26px;
  width: auto;
  flex-shrink: 0;
  display: block;
}

.logo-shimmer {
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  /* Tokenised, not recoloured. --nx-logo-white IS #F0EEFF: the cool white of
     the navigation lockup, which is deliberate and matches the inline logo SVG
     on every page and the "white + gold" website variant in D-28. It is the one
     place the cool white legitimately survives the move to warm parchment.
     These were raw hex bypassing the token system; now they are traceable. */
  background: linear-gradient(
    90deg,
    var(--nx-logo-white) 0%,  var(--nx-logo-white) 28%,
    var(--gold) 41%, var(--gold-light) 50%,
    var(--gold) 59%, var(--nx-logo-white) 72%,
    var(--nx-logo-white) 100%
  );
  background-size: 260% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  display: inline-block;
  animation: logoShimmer 5s ease-in-out infinite;
}

@keyframes logoShimmer {
  0%,  28% { background-position: 170% center; }
  68%, 100% { background-position: -70% center; }
}

@media (prefers-reduced-motion: reduce) {
  .logo-shimmer {
    animation: none;
    background: none;
    -webkit-text-fill-color: var(--nx-logo-white);
    color: var(--nx-logo-white);
  }
}

.nav__links {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

.nav__links a {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-decoration: none;
  transition: color var(--transition-fast);
  position: relative;
}

.nav__links a:hover { color: var(--text-primary); }

.nav__links a::after {
  content: "";
  position: absolute;
  left: 0; bottom: -6px;
  width: 0; height: 2px;
  background: var(--gradient-gold);
  border-radius: 2px;
  transition: width var(--transition-base);
}
.nav__links a:hover::after { width: 100%; }

.nav__actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.lang-toggle {
  font-size: var(--font-size-xs);
  font-weight: 500;
  color: var(--gold);
  background: var(--gold-subtle);
  border: 1px solid var(--border-gold);
  border-radius: var(--radius-sm);
  padding: 5px 10px;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast),
              border-color var(--transition-fast);
  min-width: 38px;
  /* Same technique as the hamburger: the pill keeps its 26px height, the
     invisible pseudo-element gives it a 44px touch target. */
  position: relative;
}
.lang-toggle::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 100%; height: 44px;
  min-width: 44px;
  transform: translate(-50%, -50%);
}

.lang-toggle:hover {
  background: var(--gold-strong);
  border-color: var(--gold-border-hover);
  color: var(--gold-light);
}

.nav__hamburger {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
  /* Hit area only. The bars stay 32px tall; the pseudo-element carries the
     touch target up to 44px without changing anything visible. */
  position: relative;
}
.nav__hamburger::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}

.nav__hamburger span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--text-primary);
  border-radius: 2px;
  transition: transform var(--transition-base), opacity var(--transition-base);
}

/* Hamburger -> X when menu open */
.nav__hamburger.is-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav__hamburger.is-open span:nth-child(2) { opacity: 0; }
.nav__hamburger.is-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Mobile fullscreen overlay menu */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background: var(--gradient-bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-12px);
  transition: opacity var(--transition-base), transform var(--transition-base), visibility var(--transition-base);
}

.mobile-menu.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.mobile-menu a {
  font-size: var(--font-size-2xl);
  font-weight: 600;
  color: var(--text-primary);
  text-decoration: none;
  transition: color var(--transition-fast);
}
.mobile-menu a:hover { color: var(--gold-light); }

.mobile-menu .btn { margin-top: var(--space-sm); }

.mobile-menu__close {
  position: absolute;
  top: 24px; right: 24px;
  background: none; border: none;
  color: var(--text-secondary);
  font-size: 32px; line-height: 1;
  cursor: pointer;
  padding: 8px;
}
.mobile-menu__close:hover { color: var(--text-primary); }

/* ---------------------------------------------------------------------
   5. Button System
   --------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-family: var(--font-sans);
  font-weight: 500;
  border-radius: var(--radius-md);
  cursor: pointer;
  text-decoration: none;
  transition: transform var(--transition-base), box-shadow var(--transition-base),
              background var(--transition-base), color var(--transition-base),
              border-color var(--transition-base);
  white-space: nowrap;
  border: none;
  line-height: 1.2;
}

.btn--sm  { font-size: var(--font-size-sm);   padding: 8px 16px;  min-height: 36px; }
.btn--md  { font-size: var(--font-size-base); padding: 12px 24px; min-height: 44px; }
.btn--lg  { font-size: var(--font-size-lg);   padding: 16px 32px; min-height: 52px; }

/* ---- Button hierarchy -----------------------------------------------------
   Four tiers, so gold keeps meaning something. One class per tier rather than
   .btn--primary doing four jobs at three emphasis levels, which is what it was
   doing before: it served the nav, the mobile menu, the featured plan and the
   contact submit identically.

     1  .nx-btn--gold      hero CTA — filled gold, untouched, the strongest
                           action in the first viewport
     2  .btn--primary      filled gold — the page's other two real conversion
                           points: the Croissance plan and the contact submit
     3  .nav__cta          restrained gold hairline — same family, clearly
                           subordinate; never competes with the hero
     4  .btn--outline      gold hairline (Studio)
        .btn--ghost        neutral hairline (Lancement, Autorité)

   No shadows anywhere: the hero establishes that emphasis comes from fill and
   contrast, not from a glow. --shadow-purple is now unreferenced. */
.btn--primary {
  background: var(--gold);
  color: var(--nx-ink);
}
.btn--primary:hover {
  transform: translateY(-1px);
  background: var(--nx-gold-satin-high);
}

.btn--outline {
  background: transparent;
  color: var(--gold);
  border: 1px solid var(--border-gold);
}
.btn--outline:hover {
  background: var(--gold-subtle);
  border-color: var(--gold-border-hover);
  color: var(--gold-light);
}

/* Tier 4 — neutral hairline. Lancement and Autorité. Warms to gold on hover so
   the whole system reacts in one accent, rather than to violet as before. */
.btn--ghost {
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
}
.btn--ghost:hover {
  color: var(--text-primary);
  border-color: var(--gold-border-hover);
}

.btn--block { width: 100%; }

/* Tier 3 — the navigation CTA and its mobile-menu twin.
   Both markup positions carry .btn--primary, so without this they would render
   as filled gold and compete with the hero CTA in the same viewport. Gold
   hairline keeps them in the same family while making them plainly secondary:
   the hero button reads as a filled block, these read as an outline.

   Measured on obsidian: --gold text is 8.35:1, so the restrained treatment
   costs nothing in legibility. Both keep their existing box, padding and
   min-height, so nav geometry and touch targets are untouched. */
.nav__cta,
.mobile-menu .btn--primary {
  background: transparent;
  color: var(--gold);
  border: 1px solid var(--gold-border);
}
.nav__cta:hover,
.mobile-menu .btn--primary:hover {
  background: var(--gold-subtle);
  border-color: var(--gold-border-hover);
  color: var(--gold-light);
}

/* ---------------------------------------------------------------------
   8. Services
   --------------------------------------------------------------------- */
.services-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
}

.service-card {
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: 32px;
  transition: transform var(--transition-base), border-color var(--transition-base),
              background var(--transition-base);
}
.service-card:hover {
  border-color: var(--gold-border-hover);
  transform: translateY(-2px);
  background: var(--bg-card-hover);
}

/* Six icons at rest stay purple — ambient. Gold arrives only on the one card
   the cursor is on, so it reads as focus rather than decoration. */
.service-card__icon {
  width: 52px; height: 52px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-card-hover);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  /* Parchment at rest, gold on hover (below). Six permanently gold icons would
     spend the accent on decoration and leave nothing for the things that
     actually want attention — prices, the featured plan, the CTAs. */
  color: var(--nx-parchment-mute);
  margin-bottom: var(--space-md);
  transition: color var(--transition-base), border-color var(--transition-base),
              background var(--transition-base);
}
.service-card__icon svg { width: 28px; height: 28px; }
.service-card:hover .service-card__icon {
  color: var(--gold-light);
  border-color: var(--gold-border);
  background: var(--gold-soft);
}

.service-card__title {
  font-size: var(--font-size-lg);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
}
.service-card__body {
  font-size: var(--font-size-base);
  color: var(--text-secondary);
  line-height: 1.6;
}

/* ---------------------------------------------------------------------
   9. Process
   --------------------------------------------------------------------- */
.process-steps {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-md);
}
.process-step {
  position: relative;
  padding-top: var(--space-md);
}
/* dashed connector */
.process-step:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 34px; left: 56px; right: calc(-1 * var(--space-md));
  border-top: 1px dashed var(--gold-border);
  z-index: var(--z-base);
}
.process-step__num {
  position: relative;
  display: inline-block;
  font-size: var(--font-size-2xl);
  font-weight: 700;
  color: var(--gold);
  margin-bottom: var(--space-sm);
  line-height: 1;
}
.process-step__num::before {
  content: attr(data-num);
  position: absolute;
  left: -6px; top: -18px;
  font-size: var(--font-size-4xl);
  font-weight: 800;
  /* The oversized ghost numeral behind each step. Parchment rather than violet:
     it is a depth cue, not an accent, so it should read as the field catching
     light. 0.12 on the old purple field is invisible on obsidian, so it lifts
     to 0.16 to keep the same perceived presence. */
  color: var(--nx-parchment);
  opacity: 0.16;
  z-index: -1;
}
.process-step__title {
  font-size: var(--font-size-lg);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
}
.process-step__body {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

/* ---------------------------------------------------------------------
   10. Portfolio
   --------------------------------------------------------------------- */
.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
  margin-bottom: var(--space-lg);
}
/* Homepage: one real project, presented as a feature rather than a lone card in
   a grid. Scoped so work.html keeps its three-column grid untouched. */
.portfolio-feature { margin-bottom: var(--space-lg); }

.work-card--feature {
  display: grid;
  /* Screenshot leads; the detail column is the quiet half. */
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  align-items: stretch;
}
.work-card--feature .work-card__media {
  height: 100%;
  aspect-ratio: auto;
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
  border-right: 1px solid var(--border-subtle);
}
.work-card--feature .work-card__body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--space-lg);
  gap: 2px;
}
.work-card--feature .work-card__title { font-size: var(--font-size-xl); }
.work-card--feature .work-card__foot { margin-top: var(--space-md); }
.work-card {
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: border-color var(--transition-base), transform var(--transition-base),
              box-shadow var(--transition-base);
}
/* Hover is carried by the hairline warming to gold and a 2px lift. The drop
   shadow is dropped: on obsidian it reads as a dark smear rather than depth,
   and the hero establishes that this system has no shadow layer. */
.work-card:hover {
  border-color: var(--gold-border-hover);
  transform: translateY(-2px);
}

.work-card__media {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
.work-card__media img,
.work-card__media svg {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform var(--transition-slow);
}
.work-card:hover .work-card__media img,
.work-card:hover .work-card__media svg { transform: scale(1.02); }

.work-card__tag {
  position: absolute;
  top: 12px; left: 12px;
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--gold);
  background: var(--gold-subtle);
  border: 1px solid var(--border-gold);
  border-radius: 999px;
  padding: 4px 12px;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.work-card__body { padding: var(--space-md); }
.work-card__title {
  font-size: var(--font-size-lg);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 4px;
}
.work-card__meta {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  margin-bottom: var(--space-sm);
}
.work-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px var(--space-sm);
  flex-wrap: wrap;
}
/* An editorial link, so it takes the accent at rest like the hero's arrow link
   rather than sitting in violet. */
.work-card__link {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--gold);
  text-decoration: none;
  transition: color var(--transition-fast);
  white-space: nowrap;
}
.work-card__link:hover { color: var(--gold-light); }

.work-card__status {
  font-size: var(--font-size-xs);
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-secondary);
  white-space: nowrap;
}
.work-card__status::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--gold-muted);
}
.work-card__status--live::before { background: #4ADE80; }

/* coming-soon blurred overlay */
/* Design-concept media: the hero's own device composition on its obsidian
   field, echoing the new visual system inside the legacy section. Violet stays
   a faint atmosphere, same as the hero. */
.work-card__media--concept {
  background:
    radial-gradient(80% 70% at 68% 88%, rgba(var(--nx-violet-rgb), 0.20), transparent 72%),
    var(--nx-obsidian);
}
.work-card__media--concept img {
  object-fit: contain;
  padding: 6% 4%;
}

.work-card--soon .work-card__media { background: var(--bg-secondary); }
.work-card--soon .work-card__media svg { filter: blur(6px); opacity: 0.4; }
/* Placeholders read as placeholders: dashed edge, no lift, no warm glow.
   Deliberately not `opacity` on the card — that would drag the "In progress"
   label below 4.5:1. */
.work-card--soon {
  background: transparent;
  border-style: dashed;
}
.work-card--soon:hover {
  transform: none;
  border-color: var(--border-subtle);
  box-shadow: none;
}
.work-card__lock {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
}
.work-card__lock svg { width: 34px; height: 34px; }

/* ---------------------------------------------------------------------
   11. Pricing
   --------------------------------------------------------------------- */
.pricing-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: 999px;
  padding: 4px;
  margin-top: var(--space-md);
}
.pricing-toggle__btn {
  font-size: var(--font-size-sm);
  font-weight: 500;
  color: var(--text-secondary);
  background: transparent;
  border: none;
  border-radius: 999px;
  padding: 8px 20px;
  cursor: pointer;
  transition: background var(--transition-base), color var(--transition-base);
  min-height: 40px;
}
/* A segmented control, not a button. The active segment was a filled purple
   gradient, which made it the loudest thing in the Pricing section and read as
   a primary action rather than a selected state. It now reads as selected: the
   segment lifts off the track and its label goes gold, while the track keeps
   its pill silhouette so the control stays recognisable as a switch and is not
   confused with the buttons below it. */
.pricing-toggle__btn.is-active {
  background: var(--nx-surface-lift);
  color: var(--gold);
}

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-md);
  align-items: stretch;
  margin-top: var(--space-xl);
}

.price-card {
  position: relative;
  /* Grid items default to min-width:auto, so the card refuses to shrink below
     its min-content and overflows the 1fr track on narrow French viewports
     (334px card in a 305px viewport at 320). Zero it: the track, not the
     content, owns the width. */
  min-width: 0;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: 32px;
  display: flex;
  flex-direction: column;
  height: 100%;
  transition: border-color var(--transition-base), box-shadow var(--transition-base);
}
/* Non-featured cards warm slightly on hover but never out-shout Croissance. */
.price-card:not(.price-card--featured):hover { border-color: var(--gold-border); }

/* Croissance. Identity comes from four stacked signals, none of them a glow:
   a gold hairline at full strength, a surface lifted one step above its
   neighbours, the badge, and the filled CTA. The 48px gold bloom this used to
   carry is the one effect the hero system has no vocabulary for — the hero
   builds emphasis from light and contrast, never from a shadow — so it reads
   as a leftover from the purple era the moment the field goes obsidian.
   The border stays 1px like every other card: 2px plus a lift already
   out-weighs its neighbours without thickening the edge. */
.price-card--featured {
  border: 1px solid var(--gold-border-hover);
  background: var(--nx-surface-lift);
}
.price-card--featured .price-card__amount { color: var(--gold-light); }
.price-card--featured .price-card__check  { color: var(--gold); }

.price-card--studio { border-color: var(--border-gold); }
.price-card--studio .price-card__amount { font-size: var(--font-size-2xl); }
.price-card__badge {
  position: absolute;
  top: -13px; left: 50%;
  transform: translateX(-50%);
  font-size: var(--font-size-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bg-primary);
  background: var(--gradient-gold);
  border-radius: 999px;
  padding: 6px 16px;
  white-space: nowrap;
}

.price-card__name {
  font-size: var(--font-size-xl);
  font-weight: 700;
  color: var(--text-primary);
}
.price-card__subtitle {
  font-size: var(--font-size-sm);
  color: var(--gold);
  margin-bottom: var(--space-md);
}
.price-card__price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: var(--space-xs);
}
.price-card__amount {
  font-size: var(--font-size-3xl);
  font-weight: 800;
  color: var(--text-primary);
  letter-spacing: -0.02em;
}
/* Sits beside the price ("one-time", "quoted per project") — visible pricing
   copy, so --text-secondary like the rest of the pricing text. Hierarchy under
   the amount comes from size, not from dropping below AA. */
.price-card__period {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.price-card__for {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.6;
  margin-bottom: var(--space-xs);
  min-height: 44px;
}
/* Italic + xs already step this down from .price-card__for; the colour does not
   need to as well. --text-muted here measured 2.48:1 and failed AA. */
.price-card__bestfor {
  font-size: var(--font-size-xs);
  font-style: italic;
  color: var(--text-secondary);
  line-height: 1.5;
  margin-bottom: var(--space-md);
}
/* Pricing fine print — light notes below the grid.
   --text-secondary, not --text-muted: this is copy a client has to be able to
   read (hourly rates, exclusions). --text-muted is 2.48:1 on --bg-primary and
   fails AA; it stays for non-copy accents like the price period. */
.pricing-fineprint {
  max-width: 760px;
  margin: var(--space-lg) auto 0;
  text-align: center;
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}
.pricing-fineprint p { margin-bottom: var(--space-xs); }
.pricing-fineprint p:last-child { margin-bottom: 0; }
.price-card__divider {
  height: 1px;
  background: var(--border-subtle);
  margin-bottom: var(--space-md);
}
.price-card__features {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: var(--space-lg);
  flex: 1;
}
.price-card__features li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.5;
}
/* Feature ticks. Parchment on the three standard plans; the featured card
   overrides these to gold further down, which is what makes its list read as
   the recommended one. Violet here split that signal in two. */
.price-card__check {
  flex-shrink: 0;
  width: 18px; height: 18px;
  color: var(--nx-parchment-mute);
  margin-top: 1px;
}
.price-card__inherits {
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 4px;
}

/* Add-ons */
.addons {
  margin-top: var(--space-2xl);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-card);
  overflow: hidden;
}
.addons__toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  background: transparent;
  border: none;
  cursor: pointer;
  padding: var(--space-md) var(--space-lg);
  font-size: var(--font-size-lg);
  font-weight: 600;
  color: var(--text-primary);
  text-align: left;
}
.addons__toggle-hint {
  font-size: var(--font-size-sm);
  font-weight: 500;
  color: var(--gold);
  transition: color var(--transition-fast);
}
.addons__toggle:hover .addons__toggle-hint { color: var(--gold-light); }
.addons__panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height var(--transition-slow) ease;
}
.addons__panel.is-open { max-height: 720px; }
.addons__table {
  width: 100%;
  border-collapse: collapse;
}
.addons__table th,
.addons__table td {
  text-align: left;
  padding: 14px var(--space-lg);
  font-size: var(--font-size-sm);
  border-top: 1px solid var(--border-subtle);
}
.addons__table th {
  color: var(--text-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: var(--font-size-xs);
}
.addons__table td { color: var(--text-secondary); }
/* Row highlight — an 11-row price table is hard to track across without one */
.addons__table tbody tr { transition: background var(--transition-fast); }
.addons__table tbody tr:hover { background: var(--gold-soft); }
.addons__note {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-align: center;
  margin-bottom: var(--space-md);
}
/* Qualifies what an add-on price buys ("1-hour minimum", "scope varies") — copy,
   so --text-secondary rather than the 2.48:1 --text-muted. */
.addons__cell-note {
  display: block;
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  margin-top: 2px;
}
.addons__table td:last-child {
  color: var(--gold);
  font-weight: 600;
  white-space: nowrap;
  text-align: right;
}
.addons__table th:last-child { text-align: right; }

/* Care plans */
.care {
  margin-top: var(--space-2xl);
}
/* The monthly view had no top margin, so switching tabs snapped the heading up
   against the toggle. Match the one-time grid's rhythm. */
#pricingMonthly { margin-top: var(--space-xl); }

.care__head {
  position: relative;
  text-align: center;
  margin-bottom: var(--space-xl);
  padding-top: var(--space-lg);
}
/* Opens the retainer block as its own offer rather than a footnote */
.care__head::before {
  content: "";
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: min(320px, 60%);
  height: 1px;
  background: var(--gold-hairline);
}
.care__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
}
.care-card {
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--space-md) var(--space-lg);
  transition: border-color var(--transition-base);
}
.care-card:hover { border-color: var(--gold-border); }
.care-card__name {
  font-size: var(--font-size-lg);
  font-weight: 700;
  color: var(--text-primary);
}
/* One-line promise under the plan name — same emphasis role as
   .price-card__subtitle on the build packages. */
.care-card__role {
  font-size: var(--font-size-sm);
  color: var(--gold);
  margin-bottom: var(--space-xs);
}
.care-card__price {
  font-size: var(--font-size-xl);
  font-weight: 800;
  color: var(--gold-light);
}
/* Carries a real price ("or $1,250/year") — must clear AA, so --text-secondary. */
.care-card__annual {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  margin-bottom: var(--space-sm);
}
.care-card__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.5;
}
.care-card__list li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.care-card__list li::before {
  content: "\2713";
  color: var(--gold);
  font-weight: 700;
  flex-shrink: 0;
}
/* Scope note under the care grid — same role as .pricing-fineprint. */
.care__note {
  max-width: 760px;
  margin: var(--space-md) auto 0;
  text-align: center;
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  line-height: 1.6;
}
/* Same treatment as the featured price card: hairline plus lift, no bloom. */
.care-card--featured {
  position: relative;
  overflow: hidden;
  border-color: var(--gold-border-hover);
  background: var(--nx-surface-lift);
}
.care-card--featured::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--gradient-gold);
}

/* ---------------------------------------------------------------------
   12. About
   --------------------------------------------------------------------- */
.about-split {
  max-width: 760px;
}
.about-lead {
  font-size: 20px;
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.4;
  margin-bottom: 24px;
  letter-spacing: -0.01em;
}
.about__body { font-size: var(--font-size-base); color: var(--text-secondary); }
.about__body p { margin-bottom: var(--space-md); line-height: 1.75; }
.about__body strong { color: var(--text-primary); font-weight: 600; }

/* Photo column removed — About is text-only */

/* ---------------------------------------------------------------------
   13. FAQ
   --------------------------------------------------------------------- */
.faq-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 var(--space-2xl);
}
.faq-item {
  border-bottom: 1px solid var(--border-subtle);
  transition: border-color var(--transition-base);
}
.faq-item.is-open { border-bottom-color: var(--gold-border); }
.faq-item__q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  padding: var(--space-md) 0;
  font-size: var(--font-size-base);
  font-weight: 600;
  color: var(--text-primary);
  transition: color var(--transition-fast);
}
.faq-item__q:hover { color: var(--gold-light); }
.faq-item.is-open .faq-item__q { color: var(--gold); }
.faq-item__icon {
  flex-shrink: 0;
  width: 22px; height: 22px;
  position: relative;
  transition: transform var(--transition-base);
}
.faq-item__icon::before,
.faq-item__icon::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  background: var(--gold);
  border-radius: 2px;
}
.faq-item__icon::before { width: 14px; height: 2px; }
.faq-item__icon::after  { width: 2px; height: 14px; }
/* + rotates to × when open (per brief) */
.faq-item.is-open .faq-item__icon { transform: rotate(45deg); }
.faq-item.is-open .faq-item__icon::before,
.faq-item.is-open .faq-item__icon::after { background: var(--gold-light); }

.faq-item__a {
  max-height: 0;
  overflow: hidden;
  transition: max-height var(--transition-slow) ease;
}
.faq-item__a-inner {
  padding-bottom: var(--space-md);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.7;
  max-width: 90%;
}

/* ---------------------------------------------------------------------
   14. Final CTA
   --------------------------------------------------------------------- */
.final-cta {
  position: relative;
  text-align: center;
  background: var(--gradient-bg);
  overflow: hidden;
}
/* Warm wash from the top edge — keeps the purple orb but stops the section
   reading as flat violet. Sits under .final-cta__inner (z-raised). Uses the
   stronger ambient because no body text sits in the top band it lights: the
   densest part is above the heading and it has faded to near-nothing by the
   time it reaches the form fields below. */
.final-cta::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: var(--z-base);
  pointer-events: none;
  background: radial-gradient(135% 118% at 50% -48%, var(--gold-ambient-strong) 0%, transparent 92%);
  /* Same top dissolve as the section washes so the FAQ -> Contact edge blends
     rather than switching on. This section is clipped (overflow:hidden), so
     the mask is what softens its top boundary. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 170px, #000 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 170px, #000 100%);
}
/* Violet, restrained, and finally following the hero's own rule: one radial,
   peak alpha <= 0.28, parked in the outer third below the midpoint — never
   behind the copy. It used to be a 460px circle centred on the section, which
   put a violet bloom directly under the form labels and fields; on the purple
   field that read as ambient, on obsidian it read as a stain.

   Same three-stop falloff as .nx-field__violet so the page closes on the same
   atmosphere it opened with. */
.final-cta .glow-orb--center {
  width: 62%; height: 78%;
  right: -10%; bottom: -26%;
  top: auto; left: auto;
  background: radial-gradient(closest-side circle,
    rgba(var(--nx-violet-rgb), 0.24) 0%,
    rgba(var(--nx-violet-rgb), 0.12) 42%,
    rgba(var(--nx-violet-rgb), 0.035) 68%,
    transparent 100%);
}
.final-cta__inner { position: relative; z-index: var(--z-raised); max-width: 720px; margin: 0 auto; }
/* Same scale as .section-heading — it is a section heading in every respect.
   700 was the heaviest type on the page and read as the loudest thing in the
   system once the field went dark. */
.final-cta__heading {
  font-size: clamp(1.85rem, 2.9vw, 2.5rem);
  font-weight: 500;
  line-height: 1.14;
  letter-spacing: -0.022em;
  margin-bottom: var(--space-md);
}
.final-cta__sub {
  font-size: var(--font-size-lg);
  color: var(--text-secondary);
  margin: 0 auto var(--space-lg);
  max-width: 560px;
}
.final-cta__link {
  display: block;
  margin-top: var(--space-md);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.final-cta__link a { color: var(--gold); text-decoration: none; font-weight: 600; }
.final-cta__link a:hover { color: var(--gold-light); text-decoration: underline; }

/* ---------------------------------------------------------------------
   15. Footer
   --------------------------------------------------------------------- */
.footer {
  background: var(--bg-deep);
  border-top: 1px solid var(--border-subtle);
  padding: var(--space-xl) 0 var(--space-lg);
}
.footer__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--space-lg);
  margin-bottom: var(--space-lg);
}
.footer__logo {
  font-size: var(--font-size-xl);
  font-weight: 700;
  color: var(--text-primary);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.footer__logo-dot { color: var(--gold); }
.footer__tagline {
  font-size: var(--font-size-sm);
  color: var(--gold);
  font-style: italic;
  margin: var(--space-xs) 0 var(--space-md);
}
.footer__copy { font-size: var(--font-size-sm); color: var(--text-muted); }

.footer__col-title {
  font-size: var(--font-size-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin-bottom: var(--space-sm);
}
.footer__links { display: flex; flex-direction: column; gap: 10px; }
.footer__links a {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-decoration: none;
  transition: color var(--transition-fast);
  width: fit-content;
}
.footer__links a:hover { color: var(--text-primary); }

.footer__contact { display: flex; flex-direction: column; gap: var(--space-sm); align-items: flex-start; }
.footer__email {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-decoration: none;
}
.footer__email:hover { color: var(--gold-light); }

.footer__bottom {
  border-top: 1px solid var(--border-subtle);
  padding-top: var(--space-md);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  flex-wrap: wrap;
}
.footer__bottom a { font-size: var(--font-size-sm); color: var(--text-secondary); text-decoration: none; }
.footer__bottom a:hover { color: var(--text-primary); }

/* ---------------------------------------------------------------------
   16. 404
   --------------------------------------------------------------------- */
.notfound {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: var(--gradient-bg);
  padding: var(--space-lg);
  position: relative;
  overflow: hidden;
}
/* Same component, same treatment. 404 is outside this migration's scope as a
   page, but it shares this rule and the field beneath it already moved to
   obsidian with the tokens — leaving a blurred violet circle here would be a
   loose end, not a scope boundary. */
.notfound .glow-orb--center {
  width: 70%; height: 70%;
  right: -12%; bottom: -20%;
  top: auto; left: auto;
  background: radial-gradient(closest-side circle,
    rgba(var(--nx-violet-rgb), 0.20) 0%,
    rgba(var(--nx-violet-rgb), 0.10) 42%,
    rgba(var(--nx-violet-rgb), 0.03) 68%,
    transparent 100%);
}
.notfound__inner { position: relative; z-index: var(--z-raised); }
.notfound__code {
  font-size: 120px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  margin-bottom: var(--space-md);
}
.notfound__heading {
  font-size: var(--font-size-2xl);
  font-weight: 700;
  margin-bottom: var(--space-xs);
}
.notfound__sub {
  font-size: var(--font-size-lg);
  color: var(--text-secondary);
  margin-bottom: var(--space-lg);
}


/* ---------------------------------------------------------------------
   17. Contact form
   --------------------------------------------------------------------- */
.contact-form {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 540px;
  margin: 0 auto;
  text-align: left;
}
.form-row { display: flex; flex-direction: column; gap: 7px; }
.form-row label {
  font-size: var(--font-size-sm);
  font-weight: 500;
  color: var(--text-primary);
}
/* These three hardcoded 8px rather than using --radius-md, so the token change
   does not reach them. Stated explicitly instead of re-pointing at the token:
   form controls are the one place the radius choice is actually visible at 1:1,
   so it should be legible here rather than inherited from three layers away. */
.contact-form input,
.contact-form select,
.contact-form textarea {
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 2px;
  padding: 12px 14px;
  font-size: 14px;
  color: var(--text-primary);
  font-family: inherit;
  transition: border-color var(--transition-base), box-shadow var(--transition-base);
  width: 100%;
}
.contact-form input::placeholder,
.contact-form textarea::placeholder { color: var(--text-muted); }
.contact-form select option { background: var(--bg-primary); color: var(--text-primary); }
.contact-form input:focus,
.contact-form select:focus,
.contact-form textarea:focus {
  outline: none;
  border-color: var(--gold-border-hover);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
.contact-form textarea { resize: vertical; min-height: 110px; }
.field-error { font-size: var(--font-size-xs); color: var(--error); }
.field-error:empty { display: none; }

/* Also hardcoded 8px. Matches the fields they sit beside. Their semantic
   red/green fills and borders are deliberately untouched. */
.form-success,
.form-error {
  border-radius: 2px;
  padding: 16px 20px;
  font-size: 14px;
  max-width: 540px;
  margin: 0 auto var(--space-sm);
}
.form-success {
  background: var(--success-bg);
  border: 1px solid var(--success-border);
  color: var(--success);
}
.form-error {
  background: var(--error-bg);
  border: 1px solid var(--error-border);
  color: var(--error);
}
.form-success a, .form-error a { color: inherit; text-decoration: underline; }

.btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; box-shadow: none; }

.final-cta__alt {
  margin-top: var(--space-md);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.final-cta__alt a { color: var(--gold); text-decoration: none; font-weight: 600; }
.final-cta__alt a:hover { color: var(--gold-light); text-decoration: underline; }


/* ---------------------------------------------------------------------
   18. Portfolio browser frame
   --------------------------------------------------------------------- */
.portfolio-browser {
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1px solid var(--border-subtle);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}
.portfolio-browser:hover {
  transform: translateY(-4px);
  box-shadow: 0 28px 80px rgba(0, 0, 0, 0.6);
}
.portfolio-chrome {
  height: 32px;
  background: var(--browser-chrome);
  display: flex;
  align-items: center;
  padding: 0 12px;
  gap: 6px;
  border-bottom: 1px solid var(--border-subtle);
}
.portfolio-dots { display: flex; gap: 5px; flex-shrink: 0; }
.portfolio-dot { width: 9px; height: 9px; border-radius: 50%; }
.pd-r { background: var(--dot-red); }
.pd-y { background: var(--dot-yellow); }
.pd-g { background: var(--dot-green); }
/* The URL bar inside the browser frame — a downstream rectangular surface,
   hardcoded 4px. The traffic-light dots above stay circles: they are a literal
   depiction of browser chrome, not a surface this system governs. */
.portfolio-url {
  flex: 1;
  height: 18px;
  background: var(--browser-url-bg);
  border-radius: 2px;
  display: flex;
  align-items: center;
  padding: 0 9px;
  font-size: 10.5px;
  color: var(--text-muted);
  font-family: monospace;
  letter-spacing: 0.02em;
  margin-left: 8px;
}
.portfolio-screenshot {
  width: 100%;
  display: block;
  object-fit: cover;
  object-position: top;
  max-height: 320px;
}

/* Integrate the frame into the portfolio card (Qwerty Fix) */
.portfolio-grid { align-items: start; }
/* The feature keeps the browser frame pinned to the top of its half. */
.work-card--feature .work-card__media--browser { align-self: stretch; }
.work-card__media--browser {
  aspect-ratio: auto;
  overflow: visible;
  background: transparent;
  border-radius: 0;
  padding: 16px 16px 4px;
}
.work-card__media--browser .work-card__tag--framed {
  top: 56px;
  right: 26px;
  left: auto;
  background: var(--gold);
  color: var(--bg-primary);
  border-color: transparent;
}
/* the frame has its own hover lift — don't also zoom the screenshot inside it */
.work-card:hover .work-card__media--browser .portfolio-screenshot { transform: none; }

/* ---------------------------------------------------------------------
   17. Cookie consent banner
   --------------------------------------------------------------------- */
.consent-banner {
  position: fixed;
  left: 50%;
  bottom: var(--space-sm);
  transform: translateX(-50%) translateY(160%);
  width: calc(100% - var(--space-md) * 2);
  max-width: 760px;
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
  justify-content: space-between;
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-secondary);
  border: 1px solid var(--border-gold);
  border-radius: var(--radius-md);
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
  z-index: var(--z-overlay);
  opacity: 0;
  visibility: hidden;
  transition:
    transform var(--transition-slow),
    opacity var(--transition-slow),
    visibility var(--transition-slow);
}
.consent-banner.is-visible {
  transform: translateX(-50%) translateY(0);
  opacity: 1;
  visibility: visible;
}
.consent-banner__text {
  margin: 0;
  flex: 1 1 340px;
  font-size: var(--font-size-sm);
  line-height: 1.55;
  color: var(--text-secondary);
}
.consent-banner__link {
  color: var(--gold);
  text-decoration: underline;
  white-space: nowrap;
  transition: color var(--transition-fast);
}
.consent-banner__link:hover { color: var(--gold-light); }
.consent-banner__actions {
  display: flex;
  gap: var(--space-xs);
  flex-shrink: 0;
}
.consent-banner__btn {
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  font-weight: 600;
  padding: 9px 18px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  border: 1px solid transparent;
  white-space: nowrap;
  transition:
    background var(--transition-fast),
    color var(--transition-fast),
    border-color var(--transition-fast);
}
.consent-banner__btn--decline {
  background: transparent;
  color: var(--text-secondary);
  border-color: var(--border-subtle);
}
.consent-banner__btn--decline:hover {
  color: var(--text-primary);
  border-color: var(--border-gold);
}
.consent-banner__btn--accept {
  background: var(--gold);
  color: var(--bg-deep);
}
.consent-banner__btn--accept:hover { background: var(--gold-light); }

/* Footer "Cookie preferences" control — styled to match footer links */
.footer__cookie {
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  text-align: left;
  transition: color var(--transition-fast);
}
.footer__cookie:hover { color: var(--gold); }

@media (max-width: 560px) {
  .consent-banner {
    left: 0;
    bottom: 0;
    width: 100%;
    max-width: none;
    transform: translateX(0) translateY(160%);
    flex-direction: column;
    align-items: stretch;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    border-bottom: none;
  }
  .consent-banner.is-visible { transform: translateX(0) translateY(0); }
  .consent-banner__text { flex: 0 0 auto; } /* avoid 340px basis becoming column height */
  .consent-banner__actions { width: 100%; }
  .consent-banner__btn { flex: 1; }
}

/* ---------------------------------------------------------------------
   19. Ambient champagne warmth
   Large, low-opacity radial washes that read as reflected light in the room
   rather than as gold shapes. Obsidian is the field; the goal is a warm
   environment, not visible circles or dividing lines.

   Alphas were NOT reduced for the obsidian migration, which is deliberate and
   was measured rather than assumed. Judged in CIE L* — the perceptually
   uniform measure, not raw luminance — --gold-ambient at 0.14 lifts the old
   purple field by dL* 9.23, and reproducing that same lift on obsidian needs
   0.1423. The two are within a rounding error, so the approved wash strength
   carries over unchanged. (Raw luminance delta suggests otherwise and is
   misleading this close to black.)

   Each wash is a ::before pinned to its own section box, painting at
   z-index -1 inside a stacking context from `isolation: isolate`. That puts
   it behind every card background and line of copy in the section — no
   z-index work on .container. Each pseudo is inset horizontally to 0, so it
   is exactly its section's width and can never cause horizontal overflow.

   Continuity across seams: every wash carries a generous negative *bottom*
   inset, so its pseudo overhangs the section below and the two glows overlap
   through the boundary — the warmth crosses the seam instead of stopping at
   it. A downward overhang is safe because the lower section is a later
   sibling, so its content paints on top of the overhang (never the reverse);
   that is also why no wash reaches *up* into the section above. There is no
   seam hairline — section identity comes from spacing and type, not a line.

   Falloffs are wide (transparent at 80-86% of the radius) so no ellipse edge
   is ever visible; each glow just dissolves into the field. Cores alternate
   side to side down the page (right, left, centre, right, centre) so the
   light drifts as you scroll, but the overhang keeps it reading as one
   continuous field rather than a stack of lit boxes. Every core is parked
   over empty space, so measured alpha under body copy stays low and
   pricing-card text sees ~0. The hero is left alone (five orbs already).
   Gradients only — no blur filters, no animation.
   --------------------------------------------------------------------- */
#services,
#process,
#pricing,
#about {
  isolation: isolate;
}

#services::before,
#process::before,
#pricing::before,
#about::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Dissolve each wash at its own top and bottom edges. A radial whose energy
     reaches the pseudo's box edge would otherwise be sliced flat there,
     reading as a hard horizontal line at the section seam. This vertical mask
     ramps the wash from transparent at the edges to full through the middle,
     so it fades into the purple instead of cutting. The bottom ramp lands
     inside the negative-inset overhang (in the next section), so warmth still
     crosses the seam — it just crosses softly. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 150px, #000 calc(100% - 210px), transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 150px, #000 calc(100% - 210px), transparent 100%);
}

/* Services — warm pool upper-right, plus a lower-left counter-glow that
   overhangs into Process, so the left edge stays lit across the seam. */
#services::before {
  inset: 0 0 -160px;
  background:
    radial-gradient(90% 82% at 94% 0%, var(--gold-ambient) 0%, transparent 80%),
    radial-gradient(72% 66% at -2% 84%, var(--gold-ambient) 0%, transparent 84%);
}

/* Process — the left-side ribbon continues: one broad, very soft pool filling
   the empty lower half of the section and overhanging deep into the top of
   Work, dissolving that seam. The core sits well below the step text. */
#process::before {
  inset: 0 0 -240px;
  background: radial-gradient(96% 92% at 0% 72%, var(--gold-ambient) 0%, transparent 82%);
}

/* Pricing — wide, soft entrance centred above the cards. The core is lifted
   well above the section so the warmth eases in gradually from the Work
   section rather than switching on at the seam; it diffuses far down and is
   gone before the card copy. */
#pricing::before {
  inset: 0 0 -120px;
  background: radial-gradient(140% 82% at 50% -30%, var(--gold-ambient) 0%, transparent 88%);
}

/* About -> FAQ — soft pool on the right, rising toward the top of the section
   (so it meets the Pricing warmth above) and overhanging deep into FAQ below.
   The founder column is capped at 760px on the left, so the right is empty. */
#about::before {
  inset: 0 0 -240px;
  background: radial-gradient(78% 96% at 96% 34%, var(--gold-ambient) 0%, transparent 82%);
}

/* Phones: fewer, softer glows and no cross-section overhang (a tall page of
   narrow sections does not need it). The ambient core drops by half, the
   services counter-glow collapses to a single top pool, and the Process and
   About washes are dropped entirely — leaving one soft pool below the hero,
   one behind the pricing heading, and the CTA top-light. */
@media (max-width: 768px) {
  :root {
    --gold-ambient:        rgba(196, 163, 90, 0.07);
    --gold-ambient-strong: rgba(196, 163, 90, 0.09);
  }

  #services::before {
    inset: 0;
    background: radial-gradient(100% 66% at 88% 0%, var(--gold-ambient) 0%, transparent 80%);
  }
  #pricing::before { inset: 0; }
  #process::before,
  #about::before { display: none; }
}




/* ===========================================================================
   NEXORA HERO — first implementation of the modern visual system.
   Generated from prototypes/hero-redesign/hero/styles.css (design frozen).

   Namespaced `nx-` throughout so it cannot collide with the legacy purple
   homepage: production already declares .hero, .btn, .nav, .nav__inner and
   .nav__links, and animations.css declares a dead .hero__device. The --nx-*
   token prefix was already collision-free.

   The legacy hero (the old "6. Hero" section, .nx-orb, .hero-rw*) has been
   removed; this is now the only hero implementation.
   =========================================================================== */

/* --- 0  Hero-scoped reset ------------------------------------------------ */
.nx-hero a { text-decoration: none; }

/* --- 1  Tokens ----------------------------------------------------------- */
:root {
  /* Foundation */
  --nx-obsidian:        #08070D;
  --nx-obsidian-rgb:    8, 7, 13;
  --nx-obsidian-deep:   #050409;
  --nx-charcoal:        #0E0C16;

  /* Gold — brand-locked */
  --nx-gold:            #C4A35A;
  --nx-gold-rgb:        196, 163, 90;
  --nx-gold-high-rgb:   227, 203, 148;
  --nx-gold-satin-high: #E3CB94;
  --nx-hairline-lead:   rgba(var(--nx-gold-high-rgb), 0.62);
  --nx-hairline-trail:  rgba(var(--nx-gold-rgb),      0.46);
  --nx-hairline-soft:   rgba(var(--nx-gold-rgb),      0.20);
  --nx-gold-wash:       rgba(var(--nx-gold-high-rgb), 0.055);

  /* Violet — atmosphere only. One radial, peak alpha <= 0.28, outer third,
     below the midpoint. Never an edge, a border or a shadow. */
  --nx-violet-rgb:      117, 52, 225;

  /* Light. Alphas are chosen from measured contrast on obsidian, not by eye:
     0.62 = 7.0:1 · 0.70 = 8.8:1 · 0.72 = 9.3:1 · 0.80 = 11.3:1 · 1.0 = 17.8:1 */
  --nx-parchment:       #F5F1EA;
  --nx-parchment-rgb:   245, 241, 234;
  --nx-parchment-dim:   rgba(var(--nx-parchment-rgb), 0.62);
  --nx-parchment-mute:  rgba(var(--nx-parchment-rgb), 0.70);
  --nx-parchment-lead:  rgba(var(--nx-parchment-rgb), 0.72);
  --nx-parchment-head:  rgba(var(--nx-parchment-rgb), 0.80);
  --nx-ink:             #1A1712;

  /* The cool white of the website navigation lockup. Not a brand-token colour:
     it is the value the live site already uses for the mark and wordmark. */
  --nx-logo-white:      #F0EEFF;

  /* Hairlines on obsidian */
  --nx-line:            rgba(var(--nx-parchment-rgb), 0.16);
  --nx-line-hover:      rgba(var(--nx-parchment-rgb), 0.34);
  --nx-line-soft:       rgba(var(--nx-parchment-rgb), 0.10);
  --nx-line-faint:      rgba(var(--nx-parchment-rgb), 0.055);

  /* Geometry — the mark's own shear, arctan(8/46). Never rounded to 10deg. */
  --nx-angle:           9.87deg;
  --nx-label-track:     0.22em;
  --nx-font:            'Inter', system-ui, -apple-system, sans-serif;

  /* Layout */
  --gut:                clamp(1.25rem, 4.2vw, 4.5rem);
  --maxw:               1560px;
  --nx-nav-h:           76px;

  /* --- Shared homepage surfaces (added by the visual migration) -----------
     The hero has no cards — it is hairlines on an open field — so the surface
     tier below did not exist yet. These extend the hero's own vocabulary
     rather than inventing one: parchment alpha over obsidian, never violet.
     Violet is atmosphere only and must never take a fill, an edge or a shadow.

     Measured over --nx-obsidian (#08070D):
       0.025  a card reads as lifted without becoming a grey box
       0.045  hover, still below the hairline's own weight
       0.060  the one "featured" lift; pairs with a gold hairline, not a glow
       0.48   the quietest tier that is still legible as copy — see below  */
  --nx-surface:         rgba(var(--nx-parchment-rgb), 0.025);
  --nx-surface-hover:   rgba(var(--nx-parchment-rgb), 0.045);
  --nx-surface-lift:    rgba(var(--nx-parchment-rgb), 0.060);

  /* The old --text-muted was rgba(240,238,255,0.30) = 2.48:1 on the purple
     field, and CLAUDE.md correctly called it "not usable for copy". Five places
     used it for copy anyway: .footer__copy, .footer__col-title,
     .price-card__inherits, .portfolio-url and the add-ons table header.

     A straight port to obsidian only reaches 2.78:1, so the failure would have
     survived the migration. 0.48 measures 4.53:1 on --nx-field and 4.51:1 on
     the deeper footer plane, clearing AA on both, while staying clearly below
     --nx-parchment-dim (0.62). Three legible tiers instead of two plus a
     decorative one. */
  --nx-parchment-faint: rgba(var(--nx-parchment-rgb), 0.48);

  /* The page field, stated once. Sections are all transparent and inherit it
     from body, so this single token is the whole homepage background. */
  --nx-field:           var(--nx-obsidian);
  --nx-field-deep:      var(--nx-obsidian-deep);
  --nx-field-raised:    var(--nx-charcoal);
}

/* --- 4  Buttons and links ------------------------------------------------ */
.nx-btn {
  display: inline-block;
  font-size: 0.98rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  padding: 1.05rem 2rem;
  transition: background-color 0.2s ease, color 0.2s ease;
}

/* Both actions are gold; dominance comes from size and air, not from taking the
   colour away. The hero CTA covers ~2.4x the area of the nav one. Hover changes
   background-colour only, so it can never shift layout. */
.nx-btn--gold { background: var(--nx-gold); color: var(--nx-ink); }
.nx-btn--gold:hover { background: var(--nx-gold-satin-high); }


.nx-btn--hero {
  padding: 1.22rem 2.72rem;
  font-size: 1.06rem;
  font-weight: 600;
  letter-spacing: 0.005em;
  min-height: 58px;
}

.nx-link-arrow {
  font-size: 0.98rem;
  color: var(--nx-parchment);
  padding-bottom: 3px;
  border-bottom: 1px solid var(--nx-hairline-soft);
  transition: border-color 0.2s ease;
}
.nx-link-arrow::after { content: " →"; color: var(--nx-gold); }
.nx-link-arrow:hover { border-color: var(--nx-gold); }

/* --- 5  Hero shell ------------------------------------------------------- */
.nx-hero {
  position: relative;
  isolation: isolate;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-block: calc(var(--nx-nav-h) + clamp(2rem, 5vh, 4.5rem))
                 clamp(2rem, 5vh, 4.5rem);
  overflow: hidden;
}

/* --- 6  Atmosphere ------------------------------------------------------- */
/* Four static layers plus one tiled plate. No filters, no blend modes, no blur:
   everything here is paint the compositor can cache. */
.nx-field {
  position: absolute;
  inset: -1px;
  z-index: -1;
  /* Obsidian only. See note above: the prototype's two radials were never
     rendered by any browser, so reviving them here would change the design. */
  background: var(--nx-obsidian);
}

/* The atmosphere resolves into the next section instead of being sliced by the
   hero's edge — the aperture cuts and the violet both settle back to obsidian. */
.nx-field::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 17%;
  background: linear-gradient(to bottom,
    rgba(var(--nx-obsidian-rgb), 0), var(--nx-obsidian));
}

/* The single permitted violet radial: outer third, below the midpoint, behind
   the devices so it reads as the light they stand in. Peak alpha 0.26. */
.nx-field__violet {
  position: absolute;
  right: -8%;
  bottom: -34%;
  width: 68%;
  height: 92%;
  background: radial-gradient(closest-side circle,
    rgba(var(--nx-violet-rgb), 0.26) 0%,
    rgba(var(--nx-violet-rgb), 0.13) 42%,
    rgba(var(--nx-violet-rgb), 0.04) 68%,
    transparent 100%);
  will-change: transform;
  animation: nx-hero-drift 34s ease-in-out infinite;
}

/* The aperture — the brand's own structural cut, sheared at the mark's angle.
   Its leading hairline is the seam between the copy and the devices. Position
   is tuned per breakpoint so the cut always passes through field, never type. */
.nx-field__aperture {
  position: absolute;
  top: -12%;
  bottom: -12%;
  left: 50%;
  width: 30%;
  background: var(--nx-gold-wash);
  border-left: 1px solid var(--nx-hairline-lead);
  border-right: 1px solid var(--nx-hairline-trail);
  transform: skewX(calc(-1 * var(--nx-angle)));
  opacity: 0.74;
}

.nx-field__grain {
  position: absolute;
  inset: 0;
  background-image: url('../images/hero-grain-256.webp');
  background-repeat: repeat;
  background-size: 256px 256px;
  opacity: 0.042;
}

/* --- 7  Hero layout ------------------------------------------------------ */
/* Three grid children, not two: the proof row is a sibling of the copy so it can
   sit under it on desktop and move after the devices on phones. */
.nx-hero__inner {
  position: relative;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding-inline: var(--gut);
  display: grid;
  grid-template-columns: minmax(0, 46fr) minmax(0, 54fr);
  grid-template-areas:
    "copy  stage"
    "proof stage";
  align-items: start;
  column-gap: clamp(1.5rem, 3vw, 3.5rem);
  row-gap: clamp(1.8rem, 3.4vh, 2.6rem);
}

.nx-hero__copy {
  grid-area: copy;
  max-width: 38rem;
}

.nx-eyebrow {
  margin: 0 0 1.5rem;
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: var(--nx-label-track);
  text-transform: uppercase;
  color: var(--nx-gold);
}

/* Two-tone hierarchy: the setup dim, the payoff at full parchment. This is how
   the brand makes emphasis — never by setting a clause in gold. */
.nx-hero__title {
  margin: 0;
  font-size: clamp(2.3rem, 3.34vw, 3.5rem);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.022em;
}
.nx-hero__title > span { display: block; }
.nx-t-setup  { color: var(--nx-parchment-head); }
.nx-t-payoff { color: var(--nx-parchment); }

.nx-hero__rule {
  display: block;
  width: 84px;
  height: 1px;
  margin: 1.9rem 0 0;
  background: var(--nx-hairline-soft);
}

.nx-hero__lead {
  margin: 1.75rem 0 0;
  max-width: 44ch;
  font-size: clamp(1.02rem, 1.1vw, 1.15rem);
  line-height: 1.62;
  color: var(--nx-parchment-lead);
}

.nx-hero__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(1.75rem, 2.8vw, 2.6rem);
  margin-top: clamp(2.4rem, 4vh, 3.2rem);
}

.nx-proof {
  grid-area: proof;
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem 1.8rem;
  margin: 0;
  padding: 1.4rem 0 0;
  border-top: 1px solid var(--nx-line-soft);
  list-style: none;
  font-size: 0.9rem;
  letter-spacing: 0.01em;
  color: var(--nx-parchment-mute);
}
.nx-proof li { position: relative; padding-left: 1.05rem; white-space: nowrap; }
.nx-proof li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6em;
  width: 5px;
  height: 5px;
  background: var(--nx-gold);
  opacity: 0.85;
}

/* --- 8  Devices ---------------------------------------------------------- */
/* The export is used unmodified: scaled, positioned and art-directed by CSS
   only. No filter, shadow or reflection is added — the asset's baked lighting
   is the point, and a second light source would fight it.

   No CSS aspect-ratio is set. A CSS ratio can disagree with whichever source
   <picture> currently has loaded during a live resize and stretch the image;
   intrinsic sizing comes from the width/height attributes instead. */
.nx-hero__stage {
  grid-area: stage;
  align-self: center;
  position: relative;
  /* the composition is allowed past the right edge so it reads as continuing */
  margin-right: calc(-1 * var(--gut));
}

.nx-hero__stage picture { display: block; }

/* >=1281px. 124% cut ~14% of the laptop body; 112% keeps the bleed to the last
   few percent of the base, so the right edge reads intentional. */
.nx-hero__devices {
  width: 112%;
  max-width: none;
  height: auto;
  will-change: transform;
  animation: nx-hero-float 13s ease-in-out infinite;
}

/* --- 10  Motion ---------------------------------------------------------- */
/* Two compositor-only transforms; neither repaints. The static composition is
   the real design — motion adds nothing the layout depends on. */
@keyframes nx-hero-float {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(0, -9px, 0); }
}
@keyframes nx-hero-drift {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(-2.2%, -3%, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .nx-hero__devices, .nx-field__violet { animation: none; will-change: auto; }
  * { transition: none !important; }
}

/* ============================================================== 11  1280px */
/* Contained from here down: the whole silhouette resolves inside the viewport.
   104% clips only transparent margin — the master's visible extent ends at
   95.34% of its width, so the ceiling before real clipping is 104.89%.
   The grid ratio changes one step later, at 1180, so dragging the window steps
   ~7% twice rather than ~17% once. */
@media (max-width: 1280px) {
  .nx-hero__devices { width: 104%; }
}

/* ------------------------------------------------------------------ 1180px */
@media (max-width: 1180px) {
  .nx-hero__inner {
    grid-template-columns: minmax(0, 50fr) minmax(0, 50fr);
    column-gap: clamp(1.25rem, 2.2vw, 2rem);
  }
  /* The left column is too narrow here for a three-item row; it wrapped 2+1 and
     read as an orphan. Stacking is the deliberate alternative. */
  .nx-proof {
    flex-direction: column;
    gap: 0.55rem;
    font-size: 0.86rem;
  }
}

/* ------------------------------------------------------------------- 900px */
/* Tablet: single column, devices under the copy, proof still with the copy. */
@media (max-width: 900px) {

  .nx-hero {
    min-height: 0;
    padding-block: calc(var(--nx-nav-h) + clamp(2.5rem, 6vh, 4rem)) 0;
  }
  .nx-hero__inner {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "copy" "proof" "stage";
    row-gap: clamp(2rem, 5vw, 3rem);
  }
  .nx-hero__copy { max-width: 36rem; }
  .nx-hero__title { font-size: clamp(2.6rem, 6.4vw, 3.6rem); }
  .nx-hero__lead { max-width: 46ch; }
  /* Full width here, so the compact editorial row fits again. */
  .nx-proof { flex-direction: row; gap: 0.55rem 1.8rem; font-size: 0.9rem; }

  .nx-field__aperture { left: 64%; width: 42%; opacity: 0.6; }
  .nx-field__violet { right: -18%; bottom: -18%; width: 96%; height: 62%; }

  .nx-hero__stage { margin-inline: calc(-1 * var(--gut)); }
  .nx-hero__devices { width: 100%; }
}

/* ------------------------------------------------------------------- 640px */
/* Phone: copy first and complete above the fold, then a dedicated crop of the
   same master, then the proof row. The crop is displayed whole — no container
   cropping and no negative offsets, so nothing can be sliced by accident. It
   must stay at 100% width or wider: its right edge is a deliberate cut that has
   to bleed off-screen. */
@media (max-width: 640px) {
  :root { --nx-nav-h: 72px; }

  .nx-hero { padding-block: calc(var(--nx-nav-h) + 1.6rem) 0; }
  .nx-hero__inner {
    grid-template-areas: "copy" "stage" "proof";
    row-gap: 0.6rem;
  }
  .nx-hero__copy { max-width: none; }

  .nx-eyebrow { font-size: 0.64rem; letter-spacing: 0.14em; margin-bottom: 0.85rem; }
  .nx-hero__title { font-size: clamp(2.05rem, 8.4vw, 2.6rem); letter-spacing: -0.018em; }
  .nx-hero__rule { margin-top: 1.15rem; width: 64px; }
  .nx-hero__lead { margin-top: 1rem; font-size: 1rem; line-height: 1.55; max-width: 38ch; }

  .nx-hero__actions { display: grid; justify-items: stretch; gap: 0.95rem; margin-top: 1.55rem; }
  .nx-hero__actions .nx-btn { text-align: center; }
  .nx-hero__actions .nx-link-arrow { justify-self: start; }
  .nx-btn--hero { padding: 1.1rem 1.5rem; font-size: 1.04rem; }

  /* 0.95rem at 0.70 alpha is 15.2px and 8.8:1, up from 12.2px and 4.26:1. */
  .nx-proof {
    gap: 0.45rem 1.5rem;
    padding-top: 1.2rem;
    font-size: 0.95rem;
  }

  .nx-hero__stage { margin-inline: calc(-1 * var(--gut)); }
  .nx-hero__devices { width: 100%; }

  .nx-field__aperture { left: 52%; width: 54%; opacity: 0.5; }
  .nx-field__violet { right: -30%; bottom: -6%; width: 130%; height: 52%; }
}

/* ------------------------------------------------------------------- 360px */
@media (max-width: 360px) {
  .nx-hero__title { font-size: 1.98rem; }
  .nx-eyebrow { font-size: 0.6rem; letter-spacing: 0.1em; }
  .nx-hero__lead { font-size: 0.97rem; max-width: none; }
  .nx-hero__actions { margin-top: 1.35rem; }
  .nx-btn--hero { padding: 1rem 1.2rem; font-size: 1rem; }
  .nx-proof { font-size: 0.92rem; }
}
