/* ===========================================================================
   NJAM — Not Just Any Media
   Built to Brand Palette Guide v1.2.

   Type system: Poppins 300 / 600 and JetBrains Mono 300 are the only live
   families and weights. Nothing else may appear — a 400 or 500 here would be
   synthesised, which is the faux-bold kill criterion.
   =========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;

  /* The full-bleed trick — margin-inline: calc(50% - 50vw) — measures 50vw
     against a viewport that includes the scrollbar and 50% against a parent
     that does not, so anything using it hangs over the right edge by exactly
     the scrollbar's width and the whole page gains 8px of sideways scroll.
     The hall on /systems has always done this; the case study galleries now
     do it too, which made it worth fixing rather than tolerating.
     `clip` rather than `hidden`: hidden on the root turns the document into a
     scroll container and the sticky nav stops sticking. Verified in Chrome. */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--njam-black);
  color: var(--ink-body);
  font-family: var(--font-brand);
  font-weight: var(--weight-light);
  font-size: 1.125rem;      /* 18px floor — light body below 18px is a kill criterion */
  line-height: 1.65;
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img,
video { max-width: 100%; height: auto; display: block; }

/* The renders are generated on pure black while the brand ground is Jet
   Black, eleven levels lighter — enough to draw a faint rectangle around
   every image. That is fixed once in the asset pipeline, which composites
   every derivative onto the ground colour (tools/optimise-images.mjs), so
   no CSS trickery is needed here. */

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--njam-champagne);
  outline-offset: 3px;
}

::selection {
  background: var(--njam-champagne);
  color: var(--njam-black);
}

/* --- Type scale ---------------------------------------------------------- */

h1, h2, h3, h4 {
  color: var(--ink-full);
  font-weight: var(--weight-light);
  margin: 0;
  text-wrap: balance;
}

h1 {
  font-size: clamp(2.25rem, 1.35rem + 4vw, 4.5rem);
  letter-spacing: -0.035em;
  line-height: 0.98;
}

h2 {
  font-size: clamp(1.75rem, 1.2rem + 2.4vw, 3rem);
  letter-spacing: -0.025em;
  line-height: 1.05;
}

h3 {
  font-size: clamp(1.25rem, 1.1rem + 0.7vw, 1.625rem);
  letter-spacing: -0.015em;
  line-height: 1.15;
}

h4 { font-size: 1.125rem; line-height: 1.3; }

p { margin: 0 0 1.25em; max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

/* Headline line-heights are tight (0.98 / 1.05), so following copy needs an
   explicit gap or it sits on the descenders. */
h1 + p, h2 + p, h3 + p, h4 + p { margin-top: 1.5rem; }

.lede {
  font-size: clamp(1.1875rem, 1.1rem + 0.5vw, 1.4375rem);
  line-height: 1.5;
  color: var(--ink-body);
}

.secondary { color: var(--ink-secondary); }

/* Mono is the machine voice: straplines, technical labels, receipts, status.
   Tabular numerals, ligatures off — these are codes, not prose. */
.mono {
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  letter-spacing: var(--track-machine);
  font-size: 0.8125rem;
  text-transform: uppercase;
  color: var(--ink-secondary);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'liga' 0, 'clig' 0;
}

.eyebrow {
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  letter-spacing: var(--track-machine);
  font-size: 0.75rem;
  text-transform: uppercase;
  color: var(--njam-champagne);
  margin: 0 0 1.5rem;
  font-variant-numeric: tabular-nums;
}

.njam-wordmark {
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  letter-spacing: var(--track-wordmark);
  text-transform: uppercase;
}

.njam-fullname {
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  letter-spacing: var(--track-fullname);
  text-transform: uppercase;
  font-size: 0.6875rem;
  color: var(--ink-label);
}

.njam-strapline {
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  letter-spacing: var(--track-machine);
}

/* --- Layout -------------------------------------------------------------- */

.shell {
  width: min(100% - (var(--gutter) * 2), var(--shell));
  margin-inline: auto;
}

.section { padding-block: var(--space-section); }
.section--tight { padding-block: var(--space-block); }

.section-head { margin-bottom: var(--space-block); }
.section-head p { color: var(--ink-secondary); }

.rule {
  border: 0;
  border-top: 1px solid var(--hairline-soft);
  margin: 0;
}

/* --- Skip link ----------------------------------------------------------- */

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--njam-champagne);
  color: var(--njam-black);
  padding: 0.75rem 1.25rem;
  font-family: var(--font-machine);
  font-size: 0.8125rem;
  text-decoration: none;
}
.skip:focus { left: 1rem; top: 1rem; }

/* --- Nav ----------------------------------------------------------------- */
/* Poppins SemiBold 600 carries the wordmark, navigation and action. */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(11, 11, 11, 0.82);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid var(--hairline-soft);
}

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  min-height: 72px;
}

.nav__mark {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  text-decoration: none;
  flex-shrink: 0;
}

.nav__mark img { width: 34px; height: 34px; }

.nav__mark-type {
  display: flex;
  flex-direction: column;
  gap: 3px;
  line-height: 1;
}

/* The nav says the name in full, never the abbreviation. It used to be a
   stacked lockup — NJAM at 15px over the full name at 11px — which meant the
   one place the company introduces itself led with four letters, and below
   400px the full name was hidden outright so a phone saw only "NJAM".

   The full name is wider than the abbreviation and has to share the bar with
   the Menu button, so the size and tracking step down as the viewport narrows
   rather than the name being dropped. 0.22em tracking is most of the cost at
   small sizes, which is why it tightens faster than the type does. */
.nav__mark-type .njam-fullname {
  font-size: 0.9375rem;
  letter-spacing: var(--track-wordmark);
  color: var(--ink-full);
  white-space: nowrap;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.5vw, 2.25rem);
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav__links a {
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: 0.875rem;
  letter-spacing: var(--track-action);
  color: var(--ink-secondary);
  text-decoration: none;
  padding-block: 0.5rem;
  transition: color var(--dur) var(--ease);
}

.nav__links a:hover,
.nav__links a[aria-current='page'] { color: var(--ink-full); }

/* --- The systems submenu ------------------------------------------------- */
/* Six case studies are the evidence this whole site rests on, and they were
   two clicks from every page. They are named in the furniture now.
   The parent stays a real link — /systems is a page, and a submenu that eats
   its own parent is how a section becomes unreachable on a touch screen. The
   chevron beside it is a separate button, which is what a keyboard and a
   finger use; a pointer gets the same thing on hover. */

.nav__has-menu {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.nav__disclosure {
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  padding: 0.5rem 0.15rem;
  color: var(--ink-secondary);
  cursor: pointer;
  transition: color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.nav__disclosure:hover,
.nav__has-menu:hover .nav__disclosure { color: var(--ink-full); }

.nav__disclosure[aria-expanded='true'] { transform: rotate(180deg); }

.nav__menu {
  position: absolute;
  top: calc(100% + 0.75rem);
  left: -1rem;
  z-index: 1;
  list-style: none;
  margin: 0;
  padding: 0.5rem 0;
  min-width: 13rem;
  background: rgba(11, 11, 11, 0.96);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border: 1px solid var(--hairline);

  /* Closed by default and opened by hover or by the button, with no JS
     required for the pointer case. visibility rather than display so the
     transition has something to run on, and so a closed menu is out of the
     tab order. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--dur) var(--ease),
              transform var(--dur) var(--ease),
              visibility var(--dur) var(--ease);
}

.nav__has-menu:hover .nav__menu,
.nav__has-menu:focus-within .nav__menu,
.nav__disclosure[aria-expanded='true'] + .nav__menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* The gap between the item and the panel is real space, and a pointer
   crossing it would close the menu underneath itself. This bridges it. */
.nav__has-menu::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 0.75rem;
}

.nav__menu a {
  display: block;
  padding: 0.55rem 1.15rem;
  font-size: 0.8125rem;
  white-space: nowrap;
  transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

.nav__menu a:hover,
.nav__menu a:focus-visible {
  color: var(--ink-full);
  background: rgba(255, 255, 255, 0.04);
}

@media (prefers-reduced-motion: reduce) {
  .nav__menu { transition: none; }
  .nav__disclosure { transition: color var(--dur) var(--ease); }
  .nav__disclosure[aria-expanded='true'] { transform: none; }
}

.nav__toggle {
  display: none;
  background: none;
  border: 1px solid var(--hairline);
  color: var(--ink-full);
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: 0.8125rem;
  letter-spacing: var(--track-utility);
  text-transform: uppercase;
  padding: 0.6rem 0.9rem;
  cursor: pointer;
}

/* --- Buttons ------------------------------------------------------------- */
/* Actions are Poppins SemiBold 600, sentence case, +0.02em — never the
   wordmark's tracking. 1px champagne border, transparent fill; hover fills
   champagne with black text. No gradients. */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  border: 1px solid var(--njam-champagne);
  background: transparent;
  color: var(--ink-full);
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: 0.9375rem;
  letter-spacing: var(--track-action);
  padding: 0.9375rem 1.75rem;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}

.btn:hover,
.btn:focus-visible {
  background: var(--njam-champagne);
  color: var(--njam-black);
}

.btn--quiet {
  border-color: var(--hairline);
  color: var(--ink-secondary);
}
.btn--quiet:hover,
.btn--quiet:focus-visible {
  border-color: var(--njam-champagne);
  background: transparent;
  color: var(--ink-full);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 2.5rem;
}

/* --- Hero ---------------------------------------------------------------- */
/* Copy field: roughly 38–42% protected Jet Black, type opposite the focal
   object, within 34rem and at least 6vw from the edge. */

/* Height tracks the wide art's own 2.36:1 aspect rather than the viewport.
   A viewport-height hero over-crops vertically and cuts the cube away from
   the brain — the one relationship the hero exists to show. */
.hero {
  position: relative;
  display: grid;
  align-items: center;
  min-height: clamp(520px, 44vw, 780px);
  overflow: hidden;
}

/* Grid-centred to the right so the media element is sized to the artwork
   itself rather than stretched to the hero box. With object-fit:contain the
   element stays hero-sized and the letterbox bars sit outside it — which puts
   the edge feather in the wrong place and leaves the seam visible. */
.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: grid;
  place-items: center end;
  overflow: hidden;
}

/* Contained, not cropped. The copy column can make the hero taller than the
   art's own aspect, and a cover-crop then eats the cube — separating it from
   the brain. The ground is jet black either way, so the unfilled area is
   invisible and the object always arrives whole. */
.hero__media img,
.hero__media video {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}

/* The guide's left-copy overlay, verbatim. Mirrored for right-copy assets
   via .hero--copy-right. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: linear-gradient(90deg,
    #0B0B0B 0%,
    rgba(11, 11, 11, 0.96) 32%,
    rgba(11, 11, 11, 0.55) 52%,
    rgba(11, 11, 11, 0) 72%);
}

.hero--copy-right .hero__scrim {
  background-image: linear-gradient(270deg,
    #0B0B0B 0%,
    rgba(11, 11, 11, 0.96) 32%,
    rgba(11, 11, 11, 0.55) 52%,
    rgba(11, 11, 11, 0) 72%);
}

/* The block keeps the shell's gutter (type stays ≥6vw from the edge) while
   the copy itself is capped at 34rem and held against the protected black
   field. Capping the block instead would let .shell's auto margins centre it,
   walking the type out over the object. */
.hero__body {
  position: relative;
  z-index: 2;
  padding-block: var(--space-block);
}

.hero__body > * { max-width: var(--copy-field); }

.hero__body h1 { margin-bottom: 1.5rem; }
.hero__body .lede { color: var(--ink-body); }

.hero--short { min-height: clamp(420px, 34vw, 600px); }

/* Texture heroes (macro crops with no discrete object) bleed rather than
   contain — there's no brain-cube relationship to protect, and a contained
   fit leaves a visible panel edge against the black. */
.hero--texture .hero__media { place-items: stretch; }
.hero--texture .hero__media img {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  object-fit: cover;
}

/* --- The neural hero (Home) ---------------------------------------------- */
/* One idea, one home: the living network exists here and nowhere else. */

.neural-hero {
  position: relative;
  display: grid;
  align-items: center;
  min-height: clamp(600px, 92vh, 940px);
  overflow: hidden;
}

.neural-hero__stage {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* The film is the bed. It fills the stage and is cropped rather than
   letterboxed — there is no discrete object in it to protect, so a contained
   fit would only leave a visible panel edge against the ground. The poster
   attribute carries the same frame, so the still and the film are the same
   picture and the swap is invisible. */
.neural-hero__film {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: center;
}


.neural-hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: linear-gradient(90deg,
    #0B0B0B 0%,
    rgba(11, 11, 11, 0.94) 26%,
    rgba(11, 11, 11, 0.55) 46%,
    rgba(11, 11, 11, 0) 68%);
}

.neural-hero__body {
  position: relative;
  z-index: 3;
  padding-block: var(--space-block);
}

.neural-hero__body > * { max-width: var(--copy-field); }
.neural-hero__body h1 { margin-bottom: 1.5rem; }

/* --- Receipts strip ------------------------------------------------------ */
/* Static. Counting numbers read as startup theatre; stillness reads as fact. */

/* Hairlines are drawn as cell borders, not as a 1px gap over a tinted
   background. With auto-fit, any partly-filled row leaves empty tracks —
   and the tinted background then shows through them as a solid lighter
   block where there should be nothing at all. */
.receipts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  border-block: 1px solid var(--hairline-soft);
}

.receipt {
  background: var(--njam-black);
  border-right: 1px solid var(--hairline-soft);
  padding: clamp(1.75rem, 4vw, 2.75rem) clamp(1.25rem, 3vw, 2rem);
}

.receipt:last-child { border-right: 0; }

.receipt__figure {
  font-family: var(--font-brand);
  font-weight: var(--weight-light);
  font-size: clamp(1.875rem, 1.35rem + 2.2vw, 3rem);
  line-height: 1.05;
  color: var(--ink-full);
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
}

.receipt__label {
  display: block;
  margin-top: 0.875rem;
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.75rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

/* --- Systems cards ------------------------------------------------------- */
/* Monochrome by default. The product's own accent blooms on hover, as a
   keyline and a label — never as a surface. */

.systems-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  border-top: 1px solid var(--hairline-soft);
  border-left: 1px solid var(--hairline-soft);
}

.system-card {
  --accent: var(--njam-champagne);
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  background: var(--njam-black);
  border-right: 1px solid var(--hairline-soft);
  border-bottom: 1px solid var(--hairline-soft);
  padding: clamp(1.75rem, 3vw, 2.25rem);
  text-decoration: none;
  min-height: 220px;
  transition: background-color var(--dur) var(--ease);
}

.system-card::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px solid transparent;
  pointer-events: none;
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}

.system-card:hover,
.system-card:focus-visible { background: var(--surface-raised); }

.system-card:hover::after,
.system-card:focus-visible::after {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: 0 0 28px -8px color-mix(in srgb, var(--accent) 45%, transparent);
}

.system-card__name {
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: 1.125rem;
  letter-spacing: var(--track-utility);
  text-transform: uppercase;
  color: var(--ink-full);
  transition: color var(--dur) var(--ease);
}

.system-card:hover .system-card__name,
.system-card:focus-visible .system-card__name { color: var(--accent); }

.system-card__line {
  color: var(--ink-secondary);
  font-size: 1rem;
  margin: 0;
  flex-grow: 1;
}

.system-card__receipt {
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.75rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

/* --- Split (image + copy) ------------------------------------------------ */
/* Imagery is never hidden inside a card — no frame, no chrome, just the
   object on the black field. */

.split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: clamp(2rem, 6vw, 5rem);
  align-items: center;
}

/* Heroes are full-bleed and need nothing further. Inline media sits on the
   open ground, where the render's own backdrop never matches the page
   exactly whichever way it's levelled — so its edges are feathered away
   rather than fought. */
.split__media img,
.split__media video {
  width: 100%;
  height: auto;
  -webkit-mask-image: radial-gradient(ellipse 74% 74% at 50% 50%, #0B0B0B 60%, transparent 100%);
          mask-image: radial-gradient(ellipse 74% 74% at 50% 50%, #0B0B0B 60%, transparent 100%);
}

.split--reverse .split__media { order: 2; }

/* --- Offer ladder -------------------------------------------------------- */

.ladder {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
}

.offer {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--hairline);
  padding: clamp(1.75rem, 3vw, 2.5rem);
  background: var(--njam-black);
  transition: border-color var(--dur) var(--ease),
              background-color var(--dur) var(--ease);
}

.offer:hover { border-color: var(--njam-champagne); background: var(--surface-raised); }

.offer__index {
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.75rem;
  letter-spacing: var(--track-machine);
  color: var(--ink-label);
  margin-bottom: 1.5rem;
  font-variant-numeric: tabular-nums;
}

.offer__name {
  font-size: 1.5rem;
  font-weight: var(--weight-light);
  letter-spacing: -0.02em;
  color: var(--ink-full);
  margin-bottom: 0.5rem;
}

.offer__price {
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.875rem;
  letter-spacing: var(--track-machine);
  color: var(--njam-champagne);
  margin-bottom: 1.25rem;
  font-variant-numeric: tabular-nums;
}

.offer__line { color: var(--ink-secondary); font-size: 1rem; flex-grow: 1; }

.offer .btn { margin-top: 2rem; align-self: flex-start; }

/* --- Timeline ------------------------------------------------------------ */

.timeline {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  border-top: 1px solid var(--hairline-soft);
  border-left: 1px solid var(--hairline-soft);
}

.beat {
  background: var(--njam-black);
  border-right: 1px solid var(--hairline-soft);
  border-bottom: 1px solid var(--hairline-soft);
  padding: clamp(1.5rem, 3vw, 2.25rem);
}

.beat__figure {
  font-weight: var(--weight-light);
  font-size: clamp(1.5rem, 1.15rem + 1.5vw, 2.25rem);
  color: var(--ink-full);
  line-height: 1.05;
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
}

.beat__label {
  display: block;
  margin-top: 0.75rem;
  font-family: var(--font-machine);
  font-size: 0.75rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

/* --- Orbit map ----------------------------------------------------------- */
/* Exactly one node per represented child, in that child's colour. Everything
   else stays bone, gunmetal, ivory and grey. */

.orbit {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 78vh;
  overflow: hidden;
}

.orbit__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* A bright core with a restrained halo — not a planet. A wide, opaque
   gradient reads as a lit sphere, which is the literal space scene the
   guide rules out. Small hot centre, long faint falloff. */
.orbit__core {
  position: absolute;
  left: 38%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: clamp(150px, 22vw, 320px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
              var(--njam-filament) 0%,
              rgba(255, 246, 227, 0.55) 4%,
              rgba(255, 246, 227, 0.16) 13%,
              rgba(255, 246, 227, 0.06) 28%,
              rgba(255, 246, 227, 0.02) 45%,
              transparent 66%);
  pointer-events: none;
}

.orbit__node {
  position: absolute;
  width: 15px;
  height: 15px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--accent, var(--njam-porcelain));
  transform: translate(-50%, -50%);
  cursor: pointer;
  animation: node-pulse 2s var(--ease) infinite;
}

.orbit__node::before {
  /* Enlarged hit area — the dot stays small, the tap target does not. */
  content: '';
  position: absolute;
  inset: -14px;
  border-radius: 50%;
}

@keyframes node-pulse {
  0%, 100% { opacity: 0.7; box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 40%, transparent); }
  50%      { opacity: 1;   box-shadow: 0 0 18px 3px color-mix(in srgb, var(--accent) 32%, transparent); }
}

.orbit__card {
  position: absolute;
  z-index: 5;
  width: min(320px, 74vw);
  background: rgba(11, 11, 11, 0.95);
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--accent, var(--njam-champagne));
  padding: 1.5rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity 240ms var(--ease);
  pointer-events: none;
}

.orbit__card[data-open='true'] { opacity: 1; visibility: visible; pointer-events: auto; }

.orbit__card h3 {
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: 1.0625rem;
  letter-spacing: var(--track-utility);
  text-transform: uppercase;
  color: var(--accent, var(--ink-full));
  margin-bottom: 0.625rem;
}

.orbit__card p { font-size: 0.9375rem; color: var(--ink-secondary); margin-bottom: 0.75rem; }

.orbit__card .mono { display: block; font-size: 0.75rem; }

.orbit__card a {
  display: inline-block;
  margin-top: 1rem;
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: 0.8125rem;
  letter-spacing: var(--track-action);
  color: var(--ink-full);
  text-decoration: none;
  border-bottom: 1px solid var(--accent, var(--njam-champagne));
  padding-bottom: 3px;
}

/* Static fallback: the vertical list. Always in the DOM for screen readers
   and crawlers; only visible when the orbit is not. */
.orbit-fallback { display: none; }
.orbit-fallback .systems-grid { border-top: 0; }

/* --- System detail sections ---------------------------------------------- */

/* The fallback lives on the article so a child's inline --accent can win.
   Declaring it on the mark itself overrode the inherited value and rendered
   every system in champagne — which is precisely the one place each child is
   supposed to wear its own colour. */
.system-detail {
  --accent: var(--njam-champagne);
  display: grid;
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  padding-block: var(--space-block);
  border-top: 1px solid var(--hairline-soft);
}

.system-detail__mark {
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.125rem);
  letter-spacing: var(--track-utility);
  text-transform: uppercase;
  color: var(--accent);
  line-height: 1.1;
}

.system-detail__status {
  display: inline-block;
  margin-top: 1rem;
  font-family: var(--font-machine);
  font-size: 0.75rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

.system-detail__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 2.5rem;
  margin-top: 2rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--hairline-soft);
}

.meta-item { min-width: 8rem; }

.meta-item__value {
  display: block;
  color: var(--ink-full);
  font-size: 1.25rem;
  font-variant-numeric: tabular-nums;
}

.meta-item__label {
  display: block;
  margin-top: 0.375rem;
  font-family: var(--font-machine);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

/* --- Form ---------------------------------------------------------------- */

.form { max-width: 40rem; }

.field { margin-bottom: 2rem; }

.field label {
  display: block;
  margin-bottom: 0.625rem;
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.75rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-secondary);
}

.field input,
.field textarea,
.field select {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--ink-full);
  font-family: var(--font-brand);
  font-weight: var(--weight-light);
  font-size: 1.125rem;
  padding: 0.75rem 0;
  border-radius: 0;
  transition: border-color var(--dur) var(--ease);
}

.field select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--njam-champagne) 50%),
                    linear-gradient(135deg, var(--njam-champagne) 50%, transparent 50%);
  background-position: calc(100% - 14px) calc(1.45rem), calc(100% - 8px) calc(1.45rem);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2rem;
  cursor: pointer;
}

.field select option { background: var(--njam-black); color: var(--ink-full); }

.field textarea { resize: vertical; min-height: 8rem; line-height: 1.65; }

.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-bottom-color: var(--njam-champagne);
}

.field input::placeholder,
.field textarea::placeholder { color: var(--ink-label); }

.field [aria-invalid='true'] { border-bottom-color: var(--njam-champagne); }

.form__note { font-size: 0.9375rem; color: var(--ink-secondary); margin-top: 1.5rem; }

/* Honeypot — off-screen rather than display:none, which bots skip. */
.field--trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form__status {
  margin-top: 1.5rem;
  font-family: var(--font-machine);
  font-size: 0.8125rem;
  letter-spacing: var(--track-machine);
  color: var(--njam-champagne);
}

/* --- Founder (About) ----------------------------------------------------- */
/* The one page that is unapologetically a person, so the portrait leads and
   the type sits beside it rather than over it. */

.founder {
  display: grid;
  grid-template-columns: minmax(0, 38%) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}

.founder__portrait img { width: 100%; }

.founder__intro h1 { margin-bottom: 1.5rem; }

/* --- Inline link --------------------------------------------------------- */

.inline-link {
  color: var(--ink-full);
  text-decoration: none;
  border-bottom: 1px solid var(--njam-champagne);
  padding-bottom: 2px;
  transition: color var(--dur) var(--ease);
}

.inline-link:hover { color: var(--njam-champagne); }

/* --- A child's own artwork ----------------------------------------------- */
/* Sized as a product still, not a hero. It belongs to that brand, so it is
   never levelled onto the NJAM ground or restyled. */

.system-detail__figure {
  margin-top: 2rem;
  max-width: 20rem;
}

.system-detail__figure img {
  width: 100%;
  border: 1px solid var(--hairline-soft);
}

/* --- Legal --------------------------------------------------------------- */
/* Long-form statutory copy. Quiet, generously set, and easy to scan by clause
   number — nobody reads these pages, they look things up in them. */

.legal { max-width: var(--measure); }

.legal h2 {
  font-size: clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
  letter-spacing: -0.015em;
  margin-top: var(--space-block);
  margin-bottom: 1.25rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--hairline-soft);
}

.legal h2:first-child { margin-top: 0; border-top: 0; padding-top: 0; }

.legal p { color: var(--ink-secondary); margin-bottom: 1.25em; }

.legal-updated { margin-bottom: var(--space-block); color: var(--ink-label); }

.legal-clause { padding-left: 3.25rem; text-indent: -3.25rem; }

.legal-marker {
  font-family: var(--font-machine);
  font-size: 0.8125rem;
  letter-spacing: var(--track-machine);
  color: var(--njam-champagne);
  font-variant-numeric: tabular-nums;
}

.legal-list {
  list-style: none;
  margin: 0 0 1.25em;
  padding-left: 3.25rem;
  max-width: var(--measure);
}

.legal-list li {
  color: var(--ink-secondary);
  margin-bottom: 0.5em;
  text-indent: -1.75rem;
}

/* --- Prose (About) ------------------------------------------------------- */

.prose { max-width: var(--measure); }
.prose h2 { margin-top: var(--space-block); margin-bottom: 1.25rem; }
.prose h2:first-child { margin-top: 0; }

.portrait { width: 100%; }

.heritage {
  margin-top: var(--space-block);
  padding-top: var(--space-block);
  border-top: 1px solid var(--hairline-soft);
  display: flex;
  gap: 2rem;
  align-items: center;
  flex-wrap: wrap;
}

.heritage img { width: 120px; opacity: 0.5; }
.heritage p { font-size: 0.9375rem; color: var(--ink-label); margin: 0; }

/* --- Closing ------------------------------------------------------------- */

.closing {
  position: relative;
  overflow: hidden;
  border-top: 1px solid var(--hairline-soft);
}

.closing__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: grid;
  place-items: center end;
  overflow: hidden;
}

.closing__media img,
.closing__media video {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}

.closing__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: linear-gradient(90deg,
    #0B0B0B 0%,
    rgba(11, 11, 11, 0.96) 32%,
    rgba(11, 11, 11, 0.55) 52%,
    rgba(11, 11, 11, 0) 72%);
}

.closing__body {
  position: relative;
  z-index: 2;
  padding-block: var(--space-section);
}

.closing__body > * { max-width: var(--copy-field); }

.quiet-line {
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.8125rem;
  letter-spacing: var(--track-machine);
  color: var(--ink-label);
  text-align: center;
  padding-block: var(--space-block);
  /* Overrides the paragraph measure — this line is centred across the page,
     not set to a reading column. */
  max-width: none;
  margin-inline: auto;
}

/* --- Footer -------------------------------------------------------------- */

.footer {
  border-top: 1px solid var(--hairline-soft);
  padding-block: var(--space-block);
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 2.5rem;
  align-items: flex-start;
  justify-content: space-between;
}

.footer__brand { display: flex; gap: 1rem; align-items: center; }
.footer__brand img { width: 44px; height: 44px; }

.footer__brand .njam-strapline {
  display: block;
  margin-top: 0.5rem;
  font-size: 0.75rem;
  color: var(--ink-label);
  text-transform: uppercase;
}

.footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer__links a {
  font-family: var(--font-machine);
  font-size: 0.75rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-secondary);
  text-decoration: none;
  transition: color var(--dur) var(--ease);
}

.footer__links a:hover { color: var(--ink-full); }

.footer__legal {
  margin-top: var(--space-block);
  font-family: var(--font-machine);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  color: var(--ink-label);
  text-transform: uppercase;
}

/* --- Responsive ---------------------------------------------------------- */

@media (max-width: 860px) {
  .nav__toggle { display: block; }

  .nav__links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    background: var(--njam-black);
    border-bottom: 1px solid var(--hairline-soft);
    padding: 0.5rem var(--gutter) 1.5rem;
  }

  .nav__links[data-open='true'] { display: flex; }

  .nav__links li { width: 100%; }

  .nav__links a {
    display: block;
    width: 100%;
    padding-block: 0.875rem;
    font-size: 1.0625rem;
    border-bottom: 1px solid var(--hairline-soft);
  }

  /* In the drawer the submenu is not a panel floating over the page — there
     is no page behind it to float over. It is a nested list that pushes the
     rest of the drawer down, and hover has no meaning here, so it opens only
     on the button. */
  .nav__has-menu {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 0;
    width: 100%;
  }

  .nav__has-menu > a { border-bottom: 0; }

  .nav__has-menu::after { content: none; }

  /* A 10px chevron is a 10px target unless it is given one. 48px square is
     the thumb minimum, and it is the only control in the drawer that is not
     already a full-width row. */
  .nav__disclosure {
    min-width: 3rem;
    padding-block: 0.875rem;
    align-self: stretch;
  }

  /* Removed from the flow entirely when closed, rather than hidden in place:
     in a drawer there is nothing to float over, and an invisible panel holding
     its space would leave a gap above About and Contact.
     display:none also overrides the pointer rules above — hover is not a thing
     a drawer has, and :focus-within would spring it open the moment focus
     reached the parent link. */
  .nav__menu,
  .nav__has-menu:hover .nav__menu,
  .nav__has-menu:focus-within .nav__menu {
    display: none;
    position: static;
    grid-column: 1 / -1;
    min-width: 0;
    padding: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border: 0;
    border-bottom: 1px solid var(--hairline-soft);
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: none;
  }

  .nav__disclosure[aria-expanded='true'] + .nav__menu { display: block; }

  .nav__menu a {
    padding: 0.75rem 0 0.75rem 1.25rem;
    font-size: 0.9375rem;
    border-bottom: 0;
    color: var(--ink-label);
  }

  .split--reverse .split__media { order: 0; }

  .system-detail { grid-template-columns: 1fr; }

  .founder { grid-template-columns: 1fr; }
  .founder__portrait { max-width: 24rem; }
}

/* Mobile art direction. The guide is explicit: art-direct mobile separately,
   and stack copy on plain Jet Black above the image when a crop would
   obscure or separate brain and cube. So the hero stops being an overlay —
   no type crosses the object, and the object is never centre-cropped. */
@media (max-width: 1023px) {
  /* The network keeps its own stage but the copy stops sitting over it. */
  .neural-hero {
    display: block;
    min-height: 0;
  }

  .neural-hero__stage {
    position: relative;
    inset: auto;
    height: clamp(280px, 52vw, 460px);
  }

  .neural-hero__scrim { display: none; }

  .neural-hero__body { padding-block: var(--space-block); }
  .neural-hero__body > * { max-width: none; }

  .hero,
  .hero--short {
    display: block;
    min-height: 0;
  }

  .hero__media {
    position: static;
    inset: auto;
    place-items: center;
  }

  .hero__media img,
  .hero__media video {
    width: 100%;
    height: auto;
    max-height: none;
  }

  /* Texture heroes stop bleeding once stacked — the object gets its own band. */
  .hero--texture .hero__media img {
    height: auto;
    max-height: 42vh;
    object-fit: cover;
  }

  .hero__scrim,
  .hero--copy-right .hero__scrim { display: none; }

  .hero__body { padding-block: var(--space-block); }
  .hero__body > * { max-width: none; }

  .closing__media { position: static; inset: auto; place-items: center; }
  .closing__media img,
  .closing__media video { width: 100%; height: auto; max-height: none; }
  .closing__scrim { display: none; }
  .closing__body { padding-block: var(--space-block); }
  .closing__body > * { max-width: none; }
}

/* The full name shrinks to share the bar with the Menu button rather than
   being hidden. Every current phone reports 390–430px in portrait, so the old
   400px rule meant essentially no phone ever saw the company's actual name. */
@media (max-width: 700px) {
  .nav__mark-type .njam-fullname {
    font-size: 0.8125rem;
    letter-spacing: 0.12em;
  }
}

@media (max-width: 430px) {
  .nav__inner { gap: 1rem; }
  .nav__mark { gap: 0.5rem; }
  .nav__mark img { width: 28px; height: 28px; }
  .nav__mark-type .njam-fullname {
    font-size: 0.6875rem;
    letter-spacing: 0.07em;
  }
}

/* Orbit gives way to the static list on small screens. */
@media (max-width: 700px) {
  .orbit { display: none; }
  .orbit-fallback { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  /* No drifting nodes, no particle field — the static list is the experience.
     Loops hold on their poster frame (enforced in main.js, which never calls
     play() when motion is refused). */
  .orbit { display: none; }
  .orbit-fallback { display: block; }
}

/* ===========================================================================
   v3 — the deck, the case studies, the services layer
   =========================================================================== */

/* --- The deck ------------------------------------------------------------ */
/* A horizontal rail of real product screenshots. The portfolio shows the
   children's actual faces — the cube universe is NJAM's own identity and has
   no business standing in for a product that exists.

   Scroll-snap does the work; the drag handler in deck.js is an enhancement on
   top of a rail that already scrolls, swipes and arrow-keys without it. */

.deck {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: min(80vw, 30rem);
  gap: clamp(1rem, 2.5vw, 1.75rem);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* The rail bleeds to both viewport edges so the last card is visibly cut —
     the cheapest possible signal that there is more to the right. */
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  padding-block: 0.5rem 1.5rem;
  scrollbar-width: thin;
  scrollbar-color: var(--njam-champagne-deep) transparent;
  cursor: grab;
}

.deck::-webkit-scrollbar { height: 3px; }
.deck::-webkit-scrollbar-track { background: var(--hairline-soft); }
.deck::-webkit-scrollbar-thumb { background: var(--njam-champagne-deep); }

/* No native image drag, ever — not only once a drag is recognised. Every card
   is a picture, and a picture is draggable by default, so a mouse drag that
   starts on one hands the gesture to the browser's own drag-and-drop and the
   deck stops following the cursor. deck.js cancels dragstart as well; this is
   the half that also stops the ghost image appearing. */
.deck img,
.deck picture {
  -webkit-user-drag: none;
  user-drag: none;
}

.deck[data-dragging='true'] {
  cursor: grabbing;
  scroll-snap-type: none;
  /* A drag that crosses a card must not also fire that card's link. */
  user-select: none;
}

.deck[data-dragging='true'] * { pointer-events: none; }

.deck__card {
  --accent: var(--njam-champagne);
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  text-decoration: none;
  background: var(--njam-black);
  transition: transform var(--dur) var(--ease);
}

.deck__card:hover,
.deck__card:focus-visible { transform: translateY(-4px); }

.deck__body { padding-top: 1.25rem; }

.deck__name {
  display: block;
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: 1.0625rem;
  letter-spacing: var(--track-utility);
  text-transform: uppercase;
  color: var(--accent);
}

/* Where a child's own palette has no tone that clears AA as text on black,
   its name renders in ivory and the colour stays where it only has to clear
   the 3:1 graphic bar — the node, the keyline and the status rule. */
.deck__card--ivory .deck__name { color: var(--ink-full); }

.deck__status {
  display: inline-block;
  margin-top: 0.75rem;
  padding-left: 0.75rem;
  border-left: 2px solid var(--accent);
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

.deck__line {
  margin: 0.875rem 0 0;
  font-size: 1rem;
  color: var(--ink-secondary);
  max-width: none;
}

/* --- Product stills ------------------------------------------------------ */
/* A screenshot is evidence, so it gets a frame — unlike the NJAM renders,
   which sit on the open ground. The frame says "this is a thing that runs",
   and it stops a light product UI bleeding into the page. */

.shot {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}

/* <picture> is the real child of the frame, so it has to fill it. Without
   this it is an auto-height block, the image's `height: 100%` resolves against
   nothing, and the picture sizes itself to the file — which overflows the
   frame and gets cropped by overflow:hidden, defeating object-fit entirely. */
.shot picture {
  display: block;
  width: 100%;
  height: 100%;
}

.shot img,
.shot video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

.deck__card:hover .shot,
.deck__card:focus-visible .shot {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: 0 0 34px -10px color-mix(in srgb, var(--accent) 50%, transparent);
}

/* Awaiting the real capture. Honest rather than decorative: a plate in the
   system's own colour that says what it is waiting for. It is replaced the
   moment a file lands in screenshots/<id>/ and the pipeline runs — nothing
   else on the page changes. */
.shot--pending {
  display: grid;
  place-items: center;
  gap: 0.75rem;
  background:
    radial-gradient(ellipse 120% 100% at 50% 0%,
      color-mix(in srgb, var(--accent) 12%, transparent) 0%,
      transparent 70%),
    var(--surface-raised);
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
  text-align: center;
  padding: 1.5rem;
}

.shot--pending span {
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

.shot--pending::before {
  content: '';
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 16px 2px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* --- Case study page ----------------------------------------------------- */

.case {
  --accent: var(--njam-champagne);
}

/* The system's best still, full-bleed, in its own brand world. */
.case-hero {
  position: relative;
  border-bottom: 1px solid var(--hairline-soft);
}

.case-hero .shot {
  aspect-ratio: 21 / 9;
  border: 0;
}

.case-hero__glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(0deg,
    var(--njam-black) 0%,
    rgba(11, 11, 11, 0.35) 34%,
    rgba(11, 11, 11, 0) 62%);
}

/* --- The case study banner ----------------------------------------------- */
/* Each system arrives wearing its own brand: a wide plate, made in that
   brand's own tools, opening its case study. It replaced a hero that was the
   lead product still — and most of those stills are phone captures, so the
   band was a narrow picture with two thirds of a screen's width of black
   either side of it. The still now sits in the gallery below, captioned,
   where it can do the job it is good at.
   No frame and no border: unlike a screenshot, this is not evidence that
   something runs, it is the brand's own surface, so it meets the page edge
   the way the NJAM renders do. */

.case-banner {
  position: relative;
  /* The ratio the whole set is cropped to in the pipeline. Declared here as
     well so the band holds its height before the file lands.
     Deliberately no min-height: on a block with auto width, a min-height that
     bites makes the browser widen the box to keep the ratio, and a 405px band
     in a 375px viewport gives the whole page a horizontal scrollbar. */
  aspect-ratio: 851 / 315;
  overflow: hidden;
  background: var(--njam-black);
}

.case-banner picture {
  display: block;
  width: 100%;
  height: 100%;
}

.case-banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* No fade over a banner, which is why the generator does not emit the glow
   layer for one. The standing fade is cut for a photographic crop — black to
   a third of the way up — and every one of these plates carries type in its
   bottom fifth: the Graeae's title sits 4% off the bottom edge, so even a
   shallow fade takes it. A designed plate already has a designed bottom edge,
   and the hairline under the band is enough to seat it on the page. */

/* On a phone the band is a shallow strip — around 140px — and it stays that
   way rather than being cropped taller. The type in these plates runs edge to
   edge, so trimming the sides to buy height would take the mark off one end
   and the strapline off the other, and the H1 two inches below says the same
   thing in text anyway. */

.case-head { padding-block: var(--space-block) 0; }

.case-head__name {
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: 0.875rem;
  letter-spacing: var(--track-utility);
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 1.25rem;
}

.case-head--ivory .case-head__name { color: var(--ink-full); }

.case-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2.5rem;
  margin-top: 2rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--hairline-soft);
}

.case-meta__item { min-width: 7rem; }

.case-meta__value {
  display: block;
  color: var(--ink-full);
  font-size: 1.375rem;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.case-meta__label {
  display: block;
  margin-top: 0.375rem;
  font-family: var(--font-machine);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

/* --- The interior gallery ------------------------------------------------ */
/* Every caption is load-bearing: it is the alt text's companion and the thing
   a crawler reads to understand what the product actually does. */

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
}

.gallery figure { margin: 0; }

.gallery figcaption {
  margin-top: 1rem;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink-secondary);
}

/* --- Method beats -------------------------------------------------------- */

.beats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
  counter-reset: beat;
}

.beats > div { counter-increment: beat; }

.beats h3 {
  font-size: 1.0625rem;
  letter-spacing: var(--track-utility);
  text-transform: uppercase;
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  color: var(--ink-full);
  margin-bottom: 0.875rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--hairline);
}

.beats h3::before {
  content: '0' counter(beat);
  display: block;
  margin-bottom: 0.875rem;
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  color: var(--njam-champagne);
  text-transform: none;
}

.beats p { font-size: 1rem; color: var(--ink-secondary); }

/* --- FAQ ----------------------------------------------------------------- */
/* Questions in the phrasing a buyer would actually type. <details> because it
   is open to a crawler, operable from the keyboard with nothing scripted, and
   still collapses a long page into something scannable. */

.faq { border-top: 1px solid var(--hairline-soft); }

.faq details { border-bottom: 1px solid var(--hairline-soft); }

.faq summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 2rem;
  padding-block: 1.5rem;
  cursor: pointer;
  list-style: none;
  color: var(--ink-full);
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
  line-height: 1.35;
  transition: color var(--dur) var(--ease);
}

.faq summary::-webkit-details-marker { display: none; }

.faq summary:hover { color: var(--njam-champagne); }

.faq summary::after {
  content: '+';
  flex-shrink: 0;
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 1.125rem;
  color: var(--njam-champagne);
  line-height: 1;
}

.faq details[open] summary::after { content: '–'; }

.faq__answer {
  padding-bottom: 1.75rem;
  color: var(--ink-secondary);
}

.faq__answer p { max-width: var(--measure); }

/* --- Cross-links --------------------------------------------------------- */

.crosslinks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 1px;
  background: var(--hairline-soft);
  border: 1px solid var(--hairline-soft);
}

.crosslink {
  display: block;
  background: var(--njam-black);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  text-decoration: none;
  transition: background-color var(--dur) var(--ease);
}

.crosslink:hover,
.crosslink:focus-visible { background: var(--surface-raised); }

.crosslink__label {
  display: block;
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
  margin-bottom: 0.875rem;
}

.crosslink__title {
  display: block;
  color: var(--ink-full);
  font-size: 1.125rem;
  line-height: 1.3;
  transition: color var(--dur) var(--ease);
}

.crosslink:hover .crosslink__title { color: var(--njam-champagne); }

/* --- Live link ----------------------------------------------------------- */
/* Descriptive anchors only. "Click here" tells a crawler, and a screen
   reader, precisely nothing. */

.live-link {
  display: inline-flex;
  align-items: baseline;
  gap: 0.75rem;
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: 1rem;
  letter-spacing: var(--track-action);
  color: var(--ink-full);
  text-decoration: none;
  border-bottom: 1px solid var(--accent, var(--njam-champagne));
  padding-bottom: 4px;
}

/* Vera's domain is dormant until her rebuild is live. A shelf with a dead
   link on it is worse than a shelf with none, so the anchor renders as a
   stated fact rather than a broken promise. */
.live-link[aria-disabled='true'] {
  color: var(--ink-label);
  border-bottom-style: dashed;
  cursor: default;
  pointer-events: none;
}

/* --- Services pages ------------------------------------------------------ */

.answer-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1.25rem;
  max-width: var(--measure);
}

.answer-list li {
  position: relative;
  padding-left: 2rem;
  color: var(--ink-secondary);
}

.answer-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 10px;
  height: 1px;
  background: var(--njam-champagne);
}

/* A price or a duration, said plainly. The research's whole point: the
   competitor dodges this, so saying it is the differentiator. */
.plain-answer {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  border-top: 1px solid var(--hairline-soft);
  border-left: 1px solid var(--hairline-soft);
  margin: 0;
}

.plain-answer > div {
  background: var(--njam-black);
  border-right: 1px solid var(--hairline-soft);
  border-bottom: 1px solid var(--hairline-soft);
  padding: clamp(1.5rem, 3vw, 2rem);
}

.plain-answer dt {
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
  margin-bottom: 0.875rem;
}

.plain-answer dd {
  margin: 0;
  color: var(--ink-full);
  font-size: 1.25rem;
  line-height: 1.35;
  letter-spacing: -0.015em;
}

.plain-answer dd small {
  display: block;
  margin-top: 0.625rem;
  font-size: 0.9375rem;
  line-height: 1.5;
  letter-spacing: 0;
  color: var(--ink-secondary);
}

/* --- Breadcrumb ---------------------------------------------------------- */

.crumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
  list-style: none;
  margin: 0;
  padding-block: 1.25rem;
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

.crumbs a { color: var(--ink-secondary); text-decoration: none; }
.crumbs a:hover { color: var(--ink-full); }
.crumbs li + li::before { content: '/ '; color: var(--njam-champagne-deep); }

/* --- v3 responsive ------------------------------------------------------- */

@media (max-width: 700px) {
  /* The rail SURVIVES on a phone. It used to flatten into a vertical stack
     here — the reasoning being that a one-card-wide rail is a worse carousel —
     and that was wrong twice over. The hall is already gone below this width,
     so the stack was not degrading the rail, it was removing the only way
     through the deck that a phone had: six cards became six screens of
     scrolling with no sense that they were a set. And it made this site behave
     differently from the same component on iamsambearfoot.com, where the rail
     is kept and swipes exactly as you would expect.
     Cards are sized to leave the next one visibly cut at the edge, which is
     what tells a thumb there is more to the right. */
  .deck {
    grid-auto-columns: min(84vw, 26rem);
  }

  .case-hero .shot { aspect-ratio: 16 / 10; }
}

@media (prefers-reduced-motion: reduce) {
  .deck__card:hover,
  .deck__card:focus-visible { transform: none; }
}

/* --- Phone captures ------------------------------------------------------ */
/* Most of these systems are things people use on a phone, so most of the
   supplied stills are portrait. Cropping a 1:2 capture into a 16:10 frame
   shows the top sixth of a screen and nothing of the product, so a tall still
   is presented as a tall still: contained, centred, sitting on the ground the
   way the renders do, with the frame drawn around the shot rather than around
   the crop. */

/* Deliberately not a grid. A grid row is content-sized, so `height: 100%` on
   the picture inside it resolves against a row the picture itself defined —
   circular, and the still ends up at its natural height, overflowing and being
   cropped. A plain block with a definite height from aspect-ratio gives the
   percentage something real to resolve against, and object-fit does the
   centring a grid was being asked for. */
.shot--portrait {
  aspect-ratio: 4 / 5;
  display: block;
  background:
    radial-gradient(ellipse 80% 60% at 50% 100%,
      color-mix(in srgb, var(--accent) 10%, transparent) 0%,
      transparent 70%),
    var(--njam-black);
  padding: clamp(1rem, 2.5vw, 1.75rem);
}

/* Fill the frame and letterbox inside it, rather than sizing the element to
   the picture. `height: 100%` on an auto-sized grid row resolves against
   nothing, so the image overflows and the frame's overflow:hidden crops it —
   which is the exact behaviour a contained still exists to avoid. */
.shot--portrait img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  /* The capture carries the product's own rounded corners in its pixels; this
     just keeps the cut edge from reading as a hard rectangle on the black. */
  border-radius: 6px;
}

/* In the gallery a portrait still gets more room, because the caption is
   describing something specific inside it and a thumbnail defeats the point. */
.gallery .shot--portrait { aspect-ratio: 3 / 4; }

/* --- The hall ------------------------------------------------------------ */
/* The rail above is the ground state and works on its own. Everything under
   [data-hall] is the enhancement deck.js layers on top: the same cards, the
   same links, stood up in a dark room with a floor under them.

   Only the presentation changes. No content is added, moved or hidden — a
   crawler, a screen reader and a visitor without JavaScript all see exactly
   what they saw before. */

.deck[data-hall] {
  display: block;
  position: relative;
  height: clamp(30rem, 44vw, 40rem);
  overflow: hidden;
  /* Full-bleed: the hall runs off both edges of the viewport, which is what
     makes the far cards read as continuing rather than as stopping. */
  margin-inline: calc(50% - 50vw);
  padding: 0;
  cursor: grab;
  scroll-snap-type: none;
  touch-action: pan-y;
}

.deck[data-hall][data-dragging='true'] { cursor: grabbing; }

.hall__track {
  position: absolute;
  inset: 0;
  perspective: 1500px;
  perspective-origin: 50% 42%;
  transform-style: preserve-3d;
}

.deck[data-hall] .deck__card {
  position: absolute;
  top: 6%;
  left: 50%;
  width: clamp(11rem, 17vw, 17rem);
  /* Positioned by deck.js every frame; the transition would fight it. */
  transition: none;
  transform-origin: 50% 50%;
  will-change: transform;
}

.deck[data-hall] .deck__card:hover,
.deck[data-hall] .deck__card:focus-visible { transform: none; }

/* The name goes ABOVE the card, not below it.

   Below, it sat under the reflection — which put it roughly 720px down inside
   a 634px frame with overflow:hidden, so every title on the wall was being
   clipped away and you could not tell what you were about to click. Above the
   card it is always visible, and it matches how a hall like this is read:
   a small label over each frame, and the full name of the one you are facing
   spelled out underneath the room. */
.deck[data-hall] .deck__card {
  display: flex;
  flex-direction: column;
}

.deck[data-hall] .deck__body {
  order: -1;
  padding: 0 0 0.875rem;
  text-align: center;
  /* Above the dimming scrim, so a name never becomes unreadable — but it
     still fades with depth, just more gently than the picture does. */
  position: relative;
  z-index: 4;
  opacity: calc(1 - var(--dim, 0) * 0.75);
  transition: opacity 220ms var(--ease);
}

.deck[data-hall] .deck__name { font-size: 0.9375rem; }

.deck[data-hall] .deck__status {
  margin-top: 0.375rem;
  padding-left: 0;
  border-left: 0;
}

/* The one-liner is too much text to read at an angle and in the dark. It stays
   in the markup for a crawler and a screen reader, and it is spelled out in
   full beneath the hall for whichever card you are facing. */
.deck[data-hall] .deck__line { display: none; }

/* Distance from the front, written by deck.js. The cards that have turned
   away fall back into the dark rather than competing with the one you are
   looking at. */
.deck[data-hall] .deck__card::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: rgba(11, 11, 11, var(--dim, 0));
  transition: background-color 220ms var(--ease);
}

/* The card at the front is lit: its own colour returns to the keyline, and
   the still stops being a thing in a rack and becomes the thing on offer. */
.deck[data-hall] .deck__card[data-active='true'] .shot {
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
  box-shadow: 0 0 60px -14px color-mix(in srgb, var(--accent) 60%, transparent);
}

/* The hint says what you actually do, which is not the same on a phone as on
   a laptop. "Swipe" is meaningless with a mouse and "drag or scroll" is
   meaningless with a thumb, so both are in the markup and only the true one is
   shown. The switch is deck.js's own 700px threshold — above it you are in the
   hall and you drag; below it you are on the rail and you swipe. */
.deck-hint { margin-bottom: 1.5rem; }

.deck-hint__touch { display: none; }

@media (max-width: 700px) {
  .deck-hint__pointer { display: none; }
  .deck-hint__touch { display: inline; }
}

/* A laptop with a touchscreen can do both, and there the pointer wording is
   the safer default — it is the one that stays true when someone reaches for
   the trackpad. Only a device with no fine pointer at all gets told to swipe. */
@media (min-width: 701px) and (pointer: coarse) and (hover: none) {
  .deck-hint__pointer { display: none; }
  .deck-hint__touch { display: inline; }
}

/* --- The floor ----------------------------------------------------------- */

.deck__reflection {
  position: relative;
  margin-top: 3px;
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  opacity: 0.3;
  transform: scaleY(-1);
  pointer-events: none;
  /* Masked in the element's own coordinates, which the flip then inverts —
     so this reads as opaque where it meets the card and gone by the far end. */
  -webkit-mask-image: linear-gradient(to top, #0B0B0B 0%, transparent 58%);
          mask-image: linear-gradient(to top, #0B0B0B 0%, transparent 58%);
}

.deck__reflection.shot--pending::before { opacity: 0.5; }

/* --- The stills hall ----------------------------------------------------- */
/* The case study galleries are the shelf's hall, reused: same deck.js, same
   floor, same readout. Two things differ, and both follow from the content.

   The cards are wider. The shelf's are tuned for 4:5 phone stills at 17rem;
   a 1920px product UI in that frame is smaller than the grid this replaced,
   which would be an expensive way to make something less legible. At 42rem the
   front card lands around 600px and the interface reads.

   And they are not links. A shelf card goes somewhere; a still is the thing
   itself, so there is nothing to click. */

.deck--stills[data-hall] .deck__card {
  width: clamp(22rem, 42vw, 42rem);
  top: 8%;
  cursor: default;
}

.deck--stills[data-hall] { height: clamp(32rem, 50vw, 46rem); }

/* Focus lands on the region, not on a card, so it gets a visible ring the
   same way any other focusable region does. */
.deck--stills:focus-visible {
  outline: 2px solid var(--njam-champagne);
  outline-offset: 6px;
}

/* Below the hall's threshold this is the rail, the same as the shelf. The
   caption is the point on a still, so it gets room to be read rather than
   sitting tight under the label like a deck one-liner. */
@media (max-width: 700px) {
  .deck--stills .deck__line { margin-top: 0.5rem; }
}

/* The case study hero. A 21:9 band cannot show a phone screen, so for a
   portrait lead the band becomes a stage: the still stands on the ground at a
   readable height, and the glow below it does the work the crop used to. */
.case-hero .shot--portrait {
  aspect-ratio: auto;
  height: clamp(380px, 62vh, 620px);
  padding-block: clamp(1.5rem, 4vw, 3rem) 0;
  background:
    radial-gradient(ellipse 60% 70% at 50% 55%,
      color-mix(in srgb, var(--accent) 14%, transparent) 0%,
      transparent 72%),
    var(--njam-black);
}

/* The hero still stands on the ground rather than floating in the middle of
   the band, so it is anchored to the bottom of its frame and the fade below
   meets its foot. */
.case-hero .shot--portrait img {
  object-position: center bottom;
  border-radius: 10px 10px 0 0;
}

/* The bottom fade is tuned for a full-bleed crop; over a contained still it
   would eat the bottom third of the screen being shown. */
.case-hero:has(.shot--portrait) .case-hero__glow {
  background: linear-gradient(0deg,
    var(--njam-black) 0%,
    rgba(11, 11, 11, 0.55) 12%,
    rgba(11, 11, 11, 0) 30%);
}

/* --- The readout --------------------------------------------------------- */
/* Which one you are looking at, and how many there are. Decorative: the cards
   are the real navigation, so this is hidden from assistive technology rather
   than read out twice. */

.hall__nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  padding-top: 0.5rem;
}

.hall__title {
  margin: 0;
  font-family: var(--font-brand);
  font-weight: var(--weight-wordmark);
  font-size: clamp(1.375rem, 1.1rem + 1.2vw, 2rem);
  letter-spacing: var(--track-utility);
  text-transform: uppercase;
  line-height: 1.1;
  color: var(--hall-accent, var(--ink-full));
  text-align: center;
  transition: color 260ms var(--ease);
}

/* Status, in the machine voice, with a keyline in the system's own colour —
   which is where a colour that cannot clear AA as text is always allowed to
   live: as a graphic, against a 3:1 bar rather than a 4.5:1 one. */
.hall__status {
  margin: 0;
  padding-left: 0.75rem;
  border-left: 2px solid var(--njam-champagne);
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-secondary);
  transition: border-color 260ms var(--ease);
}

.hall__line {
  margin: 0;
  max-width: 34rem;
  text-align: center;
  font-size: 1rem;
  color: var(--ink-secondary);
}

.hall__count {
  margin: 0;
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.75rem;
  letter-spacing: var(--track-machine);
  color: var(--ink-label);
  font-variant-numeric: tabular-nums;
}

.hall__dots {
  display: flex;
  gap: 0.5rem;
}

.hall__dots span {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--njam-champagne-deep);
  opacity: 0.45;
  transition: width 260ms var(--ease), opacity 260ms var(--ease),
              background-color 260ms var(--ease);
}

.hall__dots span[data-on='true'] {
  width: 22px;
  border-radius: 3px;
  opacity: 1;
  background: var(--njam-champagne);
}

/* The hall is a wide-screen room. Below 700px the deck is a vertical stack
   and deck.js never builds it; this is belt and braces for the moment between
   a resize and the next layout. */
@media (max-width: 699px) {
  .deck[data-hall] { height: auto; overflow: visible; margin-inline: 0; }
  .hall__nav { display: none; }
}

/* --- Holding page -------------------------------------------------------- */
/* A child brand with no address of its own yet, hosted here so the "visit"
   link on its case study has somewhere honest to go. It wears its own colour
   and nothing else of NJAM's except the small provenance mark at the foot —
   the parent is the landlord, not the brand. */

.holding {
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: var(--space-block) var(--gutter);
  background:
    radial-gradient(ellipse 70% 55% at 50% 42%,
      color-mix(in srgb, var(--accent) 13%, transparent) 0%,
      transparent 72%),
    var(--njam-black);
}

.holding__shell {
  display: grid;
  grid-template-columns: minmax(0, 20rem) minmax(0, 30rem);
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: center;
  justify-content: center;
  width: min(100%, var(--shell));
}

.holding__art img {
  width: 100%;
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  border-radius: 8px;
  box-shadow: 0 0 90px -24px color-mix(in srgb, var(--accent) 60%, transparent);
}

.holding__body h1 { margin-bottom: 1.5rem; }
.holding__body .eyebrow { color: var(--accent); }
.holding__body p { color: var(--ink-secondary); }
.holding__body .lede { color: var(--ink-body); }

.holding__body .btn {
  border-color: var(--accent);
}

.holding__body .btn:hover,
.holding__body .btn:focus-visible {
  background: var(--accent);
  color: var(--njam-black);
}

.holding__body .btn--quiet { border-color: var(--hairline); }

.holding__body .btn--quiet:hover,
.holding__body .btn--quiet:focus-visible {
  background: transparent;
  border-color: var(--accent);
  color: var(--ink-full);
}

/* The provenance mark. Small, monochrome, and the only NJAM thing on the page. */
.holding__mark {
  grid-column: 1 / -1;
  justify-self: center;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-top: clamp(2.5rem, 6vw, 4rem);
  text-decoration: none;
  opacity: 0.5;
  transition: opacity var(--dur) var(--ease);
}

.holding__mark:hover,
.holding__mark:focus-visible { opacity: 1; }

@media (max-width: 760px) {
  .holding__shell {
    grid-template-columns: minmax(0, 22rem);
    justify-items: center;
    text-align: center;
  }

  .holding__body p { margin-inline: auto; }
  .holding__body .btn-row { justify-content: center; }
}

/* --- Ghosts -------------------------------------------------------------- */
/* The render library, buried. A page of pure black between sections reads as
   empty rather than as considered, so these sit behind the copy at low opacity
   with every edge feathered away — the fragment brain surfacing out of the
   dark like a shadow, never like a picture placed on a wall.

   This is the "texture and banner altitude" the asset register reserves the
   fragment-brain family for: never the hero brain, never a subject in its own
   right, always something the eye finds second. */

/* Any band carrying a ghost declares it, rather than the ghost depending on
   which section class happens to be in play. The site uses .section and
   .section--tight interchangeably and the legal pages use their own; keying
   the containing block off one of them meant a ghost silently sat in front of
   the copy on the others. */
.has-ghost,
.section,
.closing {
  position: relative;
}

.ghost {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  /* Feathered on every edge, so the layer never draws its own rectangle. The
     mask is what turns a photograph into a shadow. */
  -webkit-mask-image: radial-gradient(ellipse 74% 88% at var(--ghost-x, 68%) 50%,
    #0B0B0B 12%, rgba(11, 11, 11, 0.55) 46%, transparent 80%);
          mask-image: radial-gradient(ellipse 74% 88% at var(--ghost-x, 68%) 50%,
    #0B0B0B 12%, rgba(11, 11, 11, 0.55) 46%, transparent 80%);
}

.ghost img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--ghost-x, 68%) center;
  opacity: 0.4;
}

/* Where the copy sits left, the ghost sits right, and the other way round.
   The one thing it must never do is surface under a paragraph. */
.ghost--left { --ghost-x: 24%; }
.ghost--right { --ghost-x: 76%; }
.ghost--centre { --ghost-x: 50%; }

/* Quieter still, for a band that already carries a lot of type. */
.ghost--faint img { opacity: 0.26; }

/* Content rides above it. Without this the ghost is in front of the copy and
   the whole thing is a haze rather than a shadow. */
.has-ghost > *:not(.ghost),
.section > .shell,
.closing > .shell,
.section > nav,
.section > header { position: relative; z-index: 1; }

/* A ghost is texture, not a hero, so it does not compete on a small screen —
   where the copy fills the width and there is no negative space to put it in. */
@media (max-width: 700px) {
  .ghost img { opacity: 0.22; }
}

/* The founder portrait is a photograph on a pale studio backdrop, sitting on a
   jet-black page. A hairline stops that backdrop reading as a hole punched in
   the ground — the same reason the product stills are framed and the renders
   are not. */
.founder__portrait img {
  border: 1px solid var(--hairline);
}

/* --- Promoted statement -------------------------------------------------- */
/* One answer lifted out of an accordion because it should never have needed a
   click: the thing a buyer screenshots and an AI assistant quotes. It also
   does structural work, splitting a long run of identical rows into two
   readable groups.

   Bordered on one side only. A full box would read as a callout aside; a
   single champagne rule reads as the page raising its voice. */

.statement {
  margin-block: clamp(2rem, 4vw, 3rem);
  padding: clamp(1.75rem, 3.5vw, 2.5rem) 0 clamp(1.75rem, 3.5vw, 2.5rem)
           clamp(1.5rem, 3vw, 2.5rem);
  border-left: 2px solid var(--njam-champagne);
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--njam-champagne) 7%, transparent) 0%,
    transparent 62%);
}

.statement .eyebrow { margin-bottom: 1rem; }

.statement h3 {
  font-size: clamp(1.25rem, 1.1rem + 0.8vw, 1.75rem);
  letter-spacing: -0.02em;
  margin-bottom: 1.25rem;
}

/* The answer itself, set larger than body — this is the line the page exists
   to say plainly. */
.statement__answer {
  font-size: clamp(1.1875rem, 1.1rem + 0.5vw, 1.4375rem);
  line-height: 1.5;
  color: var(--ink-full);
}

.statement p:last-child { color: var(--ink-secondary); }

/* --- FAQ group label ----------------------------------------------------- */

.faq__group {
  margin: 0 0 1rem;
  font-family: var(--font-machine);
  font-weight: var(--weight-light);
  font-size: 0.6875rem;
  letter-spacing: var(--track-machine);
  text-transform: uppercase;
  color: var(--ink-label);
}

/* The second group follows a statement, so it needs its own air above the
   label rather than butting straight onto it. */
.statement + .faq__group { margin-top: clamp(2rem, 4vw, 3rem); }
