/**
 * landing.css — the landing-page narrative layer: section rhythm, the device
 * family, the diffuser parts infographic, the reveal lens, and the shared
 * scroll-reveal.
 *
 * Loaded AFTER styles/app.css and consumes only tokens from tokens.css. It
 * owns the new sections outright; the handful of rules that adjust existing
 * sections are grouped under "SECTION RHYTHM" below, each with the reason,
 * because the page order changed (how-it-works moved up under the hero).
 *
 * Motion rules for everything here: transform and opacity only, every
 * transition disabled under prefers-reduced-motion, no scroll listeners
 * (IntersectionObserver only — see components/reveal.js).
 */

/* ====================================================================== */
/*  SECTION RHYTHM                                                        */
/* ====================================================================== */

/* The wave divider is filled with --surface-1 and now lands on the
   how-it-works band instead of the history section, so the band's own top
   hairline would cut straight across the wave's landing. Drop it there. */
.wave + .section--how {
  border-block-start: 0;
  padding-block-start: var(--sp-4);
}

/* history now follows the how-it-works band (--surface-1) rather than the
   wave, so it takes the bone ground back and its own top padding with it. */
.section--history {
  background: var(--surface-0);
  padding-block-start: calc(var(--section-pad) * 0.5);
}

/* the device family sits on the quiet band; the reveal that follows gets the
   page's one accent-tinted ground, so the drawing has its own room. */
.section--devices {
  background: var(--surface-1);
  border-block: 1px solid var(--line);
}

.section--reveal {
  background: color-mix(in srgb, var(--accent) 6%, var(--surface-0));
  /* no bottom hairline of its own: .section--infra below already draws one,
     and two adjacent 1px rules read as a doubled line. */
}

.section--infra {
  background: var(--surface-0);
}

/* sub-head inside a section, one level under the section's own h2 */
.subhead {
  margin-block-start: var(--sp-6);
  max-width: 44rem;
}
.subhead h3 {
  margin-block-end: var(--sp-2);
}
.subhead p {
  color: var(--ink-1);
  max-width: 60ch;
}

/* ====================================================================== */
/*  SCROLL REVEAL (components/reveal.js)                                  */
/* ====================================================================== */

/* The armed state is added by JS, never by markup, so no-JS and
   reduced-motion visitors get the finished layout. */
.reveal {
  opacity: 0;
  transform: translateY(18px);
}

.reveal.is-revealed {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {
  .reveal,
  .reveal.is-revealed {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ====================================================================== */
/*  DEVICE FAMILY                                                         */
/* ====================================================================== */

.device-family {
  margin-block-start: var(--sp-5);
}

.device-cards {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: 1fr;
}

.device-card {
  background: var(--surface-0);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  padding: clamp(1.25rem, 3vw, 2rem);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.device-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  /* the status pill is nowrap; let it drop under the drawing rather than
     push the card wider on a 360px screen. */
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-block-end: var(--sp-2);
}

/* Blurb with an optional circular photo to its right, vertically centred.
   The photo keeps a fixed footprint; the text column absorbs the width. */
.device-card__blurb-row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}
.device-card__blurb-row .device-card__blurb {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}
.device-card__photo {
  flex: 0 0 auto;
  width: clamp(88px, 14vw, 140px);
  aspect-ratio: 1 / 1;
  height: auto;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-2);
}
@media (max-width: 400px) {
  .device-card__blurb-row { gap: var(--sp-3); }
  .device-card__photo { width: 76px; }
}

.device-card__art {
  width: clamp(96px, 22vw, 132px);
  color: var(--accent-deep);
}
.device-card__art svg {
  width: 100%;
  height: auto;
}

.device-card__status {
  color: var(--ink-1);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 12px;
  white-space: nowrap;
  align-self: center;
}

.device-card__status--live {
  color: var(--accent-ink);
  background: var(--accent-deep);
  border-color: var(--accent-deep);
}

.device-card__name {
  font-size: var(--text-h3);
}

.device-card__blurb {
  color: var(--ink-1);
  max-width: 46ch;
}

.device-card__specs {
  margin: var(--sp-3) 0 0;
  padding-block-start: var(--sp-3);
  border-block-start: 1px solid var(--line);
  display: grid;
  gap: var(--sp-2);
}

.device-card__spec {
  display: grid;
  grid-template-columns: 8.5rem 1fr;
  gap: var(--sp-2);
  align-items: baseline;
}

.device-card__spec dt {
  color: var(--ink-1);
}

.device-card__spec dd {
  margin: 0;
  color: var(--ink-0);
  font-size: var(--text-small);
}

@media (min-width: 800px) {
  .device-cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 460px) {
  /* 8.5rem of label plus a value does not fit at 360px — stack instead. */
  .device-card__spec {
    grid-template-columns: 1fr;
    gap: 2px;
  }
}

/* ====================================================================== */
/*  DIFFUSER PARTS INFOGRAPHIC                                            */
/* ====================================================================== */

.parts {
  margin-block-start: var(--sp-6);
  display: grid;
  gap: var(--sp-5);
  grid-template-columns: 1fr;
  align-items: start;
}

/* Decoration yields first: the cutaway is aria-hidden and the numbered list
   carries every fact, so below 860px the list simply takes the full width. */
.parts-dia {
  display: none;
}

.parts__title {
  font-size: var(--text-h3);
  margin-block-end: var(--sp-2);
}

.parts__intro {
  color: var(--ink-1);
  max-width: 58ch;
}

.parts__list {
  list-style: none;
  margin: var(--sp-4) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: 1fr;
  counter-reset: none;
}

.parts__item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
}

.parts__num {
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 14%, var(--surface-0));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line));
  color: var(--accent-deep);
  font-size: var(--text-mono);
  letter-spacing: 0;
}

.parts__text {
  display: block;
}

.parts__name {
  display: block;
  color: var(--ink-0);
  font-weight: 700;
}

.parts__detail {
  display: block;
  color: var(--ink-1);
  font-size: var(--text-small);
  max-width: 44ch;
}

@media (min-width: 640px) {
  .parts__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-3) var(--sp-5);
  }
}

@media (min-width: 860px) {
  .parts {
    grid-template-columns: 240px minmax(0, 1fr);
    gap: var(--sp-6);
  }
  .parts-dia {
    display: block;
  }
}

.parts-dia svg {
  width: 100%;
  height: auto;
}

.parts-dia__housing {
  fill: color-mix(in srgb, var(--accent) 7%, var(--surface-0));
  stroke: color-mix(in srgb, var(--accent) 30%, var(--line));
  stroke-width: 1.5;
}

.parts-dia__slab {
  fill: var(--surface-0);
  stroke: var(--accent-deep);
  stroke-width: 1.5;
}

.parts-dia__num-bg {
  fill: var(--surface-0);
  stroke: color-mix(in srgb, var(--accent) 40%, var(--line));
  stroke-width: 1;
}

.parts-dia__num {
  font: 500 11px var(--font-mono), monospace;
  fill: var(--accent-deep);
}

.parts-dia__leader {
  stroke: color-mix(in srgb, var(--accent) 45%, var(--line));
  stroke-width: 1;
}

.parts-dia__mist {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-linecap: round;
}

/* ====================================================================== */
/*  SENSOR TILES — the one signal that is live today                      */
/* ====================================================================== */

.sensor-tile--live {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  background: var(--surface-0);
}

.sensor-tile__tag--live {
  color: var(--accent-ink);
  background: var(--accent-deep);
  border-color: var(--accent-deep);
}

/* ====================================================================== */
/*  DIFFUSER REVEAL LENS (components/DiffuserReveal.js)                   */
/* ====================================================================== */

.lens {
  margin: var(--sp-5) 0 0;
  max-width: 900px;
}

.lens__frame {
  --lens-x: 50%;
  --lens-y: 46%;
  /* 18% of the width; the matching 27% of the height is the same distance,
     because the frame's aspect ratio is pinned to the images' 1024:683 — so
     the window is a true circle, roughly a third of the drawing wide. */
  --lens-rx: 18%;
  --lens-ry: 27%;

  position: relative;
  aspect-ratio: 1024 / 683;
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  /* Both illustrations are JPEGs with a white background. --frame-bone is a
     constant bone tone (never redefined for dark mode — see tokens.css), so
     the visible mat below reads as a deliberate frame instead of letting the
     white bleed edge-to-edge and glare against a dark ground. In light mode
     --frame-bone already equals --surface-0, so the mat is a no-op there. */
  background: var(--frame-bone);
  touch-action: pan-y; /* a swipe still scrolls the page over the drawing */
}

.lens__layer {
  position: absolute;
  /* NOT `inset: 0` + `width/height: 100%` — for an absolutely positioned
     element the containing block is the ancestor's PADDING box, which is
     bounded by the OUTER edge of the padding (same as the border edge when
     there's no border), so `inset: 0` fills straight through any padding on
     `.lens__frame` and never leaves it visible. Insetting by the mat width
     directly is what actually reserves the gap (verified: a plain inset: 0
     + width/height: 100% child paints edge-to-edge over a padded parent). */
  inset: var(--sp-2);
  object-fit: contain;
  pointer-events: none; /* keeps pointermove's offsetX/Y relative to the frame */
  user-select: none;
}

.lens__layer--colour {
  opacity: 0;
  transition: opacity 280ms var(--ease-out);
  -webkit-mask-image: radial-gradient(
    ellipse var(--lens-rx) var(--lens-ry) at var(--lens-x) var(--lens-y),
    rgb(0 0 0) 0%,
    rgb(0 0 0) 50%,
    rgb(0 0 0 / 0.55) 74%,
    rgb(0 0 0 / 0) 100%
  );
  mask-image: radial-gradient(
    ellipse var(--lens-rx) var(--lens-ry) at var(--lens-x) var(--lens-y),
    rgb(0 0 0) 0%,
    rgb(0 0 0) 50%,
    rgb(0 0 0 / 0.55) 74%,
    rgb(0 0 0 / 0) 100%
  );
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.lens.is-active .lens__layer--colour {
  opacity: 1;
}

/* No mask support, or prefers-reduced-motion: the colour illustration simply
   stands still on top. Nothing is lost — the colour version carries the same
   legend and captions as the line version. */
.lens--static .lens__layer--colour {
  opacity: 1;
  -webkit-mask-image: none;
  mask-image: none;
}

/* Below the frame, not on it: every corner of the illustration is occupied
   (title block, legend, the Mould Detect logo, both figure captions), so a
   chip inside the frame would always cover artwork. It keeps its row in the
   flow when it fades, so nothing under it moves. */
.lens__hint {
  margin: var(--sp-3) auto 0;
  width: fit-content;
  padding: 8px 14px;
  min-height: 34px;
  display: flex;
  align-items: center;
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface-0) 88%, transparent);
  border: 1px solid var(--line);
  color: var(--ink-1);
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out);
}

/* gone for good once the pointer has actually crossed the drawing */
.lens.has-explored .lens__hint,
.lens--static .lens__hint {
  opacity: 0;
}

/* No pointer to move: the sweep runs instead and the hint would be a lie. */
@media (hover: none) {
  .lens__hint {
    display: none;
  }
}

.lens__caption {
  margin: var(--sp-3) 0 0;
  color: var(--ink-1);
  font-size: var(--text-small);
  max-width: 62ch;
}

@media (prefers-reduced-motion: reduce) {
  .lens__layer--colour,
  .lens__hint {
    transition: none;
  }
}

@media (max-width: 560px) {
  .lens__hint {
    font-size: 0.6875rem;
  }
}

/* ====================================================================== */
/*  3D VIEWER LINK (below the reveal lens) — opens /diffuser/, the X Version */
/*  three.js viewer published from mould-detect/opus-diffuser-project.     */
/* ====================================================================== */

.cta-3d {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: var(--sp-4);
  align-items: center;
  margin-block-start: var(--sp-5);
  padding: var(--sp-4);
  max-width: 62ch;
  border: 1px solid var(--line);
  border-radius: var(--r-2);
  background: var(--surface-1);
  color: var(--ink-0);
  text-decoration: none;
  transition: border-color 0.2s ease, transform 0.2s ease;
}
.cta-3d:hover { border-color: var(--accent); transform: translateY(-2px); }
.cta-3d:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.cta-3d__eyebrow { grid-column: 1; color: var(--accent-deep); font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; }
.cta-3d__title { grid-column: 1; font-family: var(--font-display); font-size: var(--text-h3); margin-block: var(--sp-1); }
.cta-3d__text { grid-column: 1; color: var(--ink-1); font-size: var(--text-small); }
.cta-3d__arrow { grid-column: 2; grid-row: 1 / span 3; font-size: 1.75rem; color: var(--accent); transition: transform 0.2s ease; }
.cta-3d:hover .cta-3d__arrow { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) {
  .cta-3d, .cta-3d__arrow { transition: none; }
  .cta-3d:hover, .cta-3d:hover .cta-3d__arrow { transform: none; }
}
