/* ------------------------------------------------------------------
   /solutions/build and /solutions/grow — the two audience hubs.

   Both reuse the trust page's section rhythm and two-column head, which is
   why those live in trust.css... except they are not trust-specific, so the
   shared parts moved here and trust.css imports nothing: this file is loaded
   alongside it only on the hubs. Everything else is landing components.

   Hub rule (page-plans.md): these reuse the landing's COMPONENTS and never
   its copy. The landing asks "what would you build?"; these answer it.
   ------------------------------------------------------------------ */

.zl-section {
  padding-block: clamp(2.75rem, 5vw, 4.5rem);
}

/* Two-column head: eyebrow and heading left, supporting line right and
   baseline-aligned. Same shape as .build-cases__head on the landing. */
.page-section__head {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  column-gap: clamp(2rem, 5vw, 5rem);
  row-gap: var(--zl-space-4);
  align-items: end;
  max-width: none;
  margin-bottom: clamp(1.75rem, 3vw, 2.75rem);
}

.page-section__head .zl-eyebrow,
.page-section__head h2 { grid-column: 1; }
.page-section__head h2 { max-width: 20ch; }

.page-section__head p {
  grid-column: 2;
  align-self: end;
  max-width: 44ch;
  padding-bottom: .35em;
  color: var(--zl-body);
  font-size: var(--zl-text-md);
  line-height: var(--zl-leading-copy);
}

@media (max-width: 56rem) {
  .page-section__head { grid-template-columns: 1fr; align-items: start; }
  .page-section__head h2,
  .page-section__head p { grid-column: 1; padding-bottom: 0; }
}

/* --- Mapping table (use case -> API, lever -> metric) ----------------- */
.map-table__wrap {
  overflow-x: auto;
  border: 1px solid var(--zl-line);
  background: var(--zl-surface);
}

.map-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--zl-text-sm);
}

.map-table th,
.map-table td {
  padding: var(--zl-space-4) clamp(.85rem, 1.6vw, 1.35rem);
  border-bottom: 1px solid var(--zl-line);
  text-align: left;
  vertical-align: top;
}

.map-table thead th {
  color: var(--zl-muted-text);
  font-family: var(--zl-font-mono);
  font-size: var(--zl-text-xs);
  font-weight: var(--zl-weight-regular);
  letter-spacing: var(--zl-tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
}

.map-table tbody th {
  color: var(--zl-ink);
  font-weight: var(--zl-weight-regular);
}

.map-table td { color: var(--zl-body); }

.map-table tbody tr:last-child th,
.map-table tbody tr:last-child td { border-bottom: 0; }

/* Access marker: open vs on request, so the Build reader can see at a glance
   which groups need a conversation. */
.map-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--zl-space-2);
  color: var(--zl-muted-text);
  font-family: var(--zl-font-mono);
  font-size: var(--zl-text-xs);
  letter-spacing: var(--zl-tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
}

.map-tag::before {
  width: .45rem;
  height: .45rem;
  border-radius: 50%;
  background: var(--zl-muted);
  content: "";
}

.map-tag[data-access="open"]::before { background: var(--zl-cobalt); }
.map-tag[data-access="request"]::before { background: var(--zl-vermilion); }

/* --- Attribute families (Grow) --------------------------------------- */
.family-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  border-top: 1px solid var(--zl-line);
  border-left: 1px solid var(--zl-line);
}

.family-card {
  display: grid;
  gap: var(--zl-space-3);
  align-content: start;
  padding: clamp(1.5rem, 2.6vw, 2.25rem);
  border-right: 1px solid var(--zl-line);
  border-bottom: 1px solid var(--zl-line);
  background: var(--zl-surface);
}

.family-card h3 {
  font-size: var(--zl-type-card-heading-size);
  line-height: var(--zl-leading-card);
}

.family-card p {
  color: var(--zl-body);
  font-size: var(--zl-text-sm);
  line-height: var(--zl-leading-copy);
}

/* --- Pipeline (how a campaign runs / integration shape) --------------- */
.flow-steps {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(.85rem, 1.6vw, 1.5rem);
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.flow-steps li {
  display: grid;
  gap: var(--zl-space-3);
  align-content: start;
  padding: clamp(1.15rem, 2.2vw, 1.75rem);
  border: 1px solid var(--zl-line);
  background: var(--zl-surface);
  color: var(--zl-body);
  font-size: var(--zl-text-sm);
  line-height: var(--zl-leading-copy);
  counter-increment: step;
}

.flow-steps li::before {
  color: var(--zl-muted-text);
  font-family: var(--zl-font-mono);
  font-size: var(--zl-text-xs);
  letter-spacing: var(--zl-tracking-label);
  content: "0" counter(step);
}

.flow-steps b {
  display: block;
  color: var(--zl-ink);
  font-weight: var(--zl-weight-regular);
  font-size: var(--zl-text-md);
}

@media (max-width: 56rem) {
  .flow-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 30rem) {
  .flow-steps { grid-template-columns: 1fr; }
}

/* --- Scope note (Build: "where the graph stops") ---------------------- */
.scope-note {
  max-width: 60ch;
  padding: clamp(1.5rem, 2.6vw, 2.25rem);
  border-left: 2px solid var(--zl-cobalt);
  background: var(--zl-surface);
  color: var(--zl-body);
  font-size: var(--zl-type-lead-size);
  line-height: var(--zl-leading-lead);
}

.scope-note strong { color: var(--zl-ink); font-weight: var(--zl-weight-regular); }

/* --- Named customers under the marquee ------------------------------- */



/* --- Scale strip -------------------------------------------------------
   The same four numbers repeat across every Build-side page. Repetition is
   deliberate: one drumbeat beats four different figures per page. */
.stat-strip {
  display: grid;
  /* Exactly 4, never auto-fit: at ~1000px auto-fit produced five tracks for
     four numbers, leaving a bordered empty cell at the end of the row.
     .stat-strip--three is the same discipline for a band of three, added when
     /grow/integrations dropped to three stats and left a quarter of the strip
     empty — the very defect the line above was written about. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border-top: 1px solid var(--zl-line);
  border-left: 1px solid var(--zl-line);
}

.stat-strip > div {
  display: grid;
  gap: .35rem;
  align-content: start;
  padding: clamp(1.1rem, 2.2vw, 1.6rem);
  border-right: 1px solid var(--zl-line);
  border-bottom: 1px solid var(--zl-line);
}

.stat-strip b {
  color: var(--zl-ink);
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  font-weight: 500;
  line-height: 1.05;
}

.stat-strip span {
  color: var(--zl-muted);
  font-size: var(--zl-text-xs);
  letter-spacing: var(--zl-tracking-label);
  text-transform: uppercase;
}

/* --- Graph layers ------------------------------------------------------
   Count first, then the name, then what it unlocks. The count leads because
   the specialty layers are the argument -- professional profiles are parity
   with every competitor, GitHub and healthcare are not. */
.layer-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1px;
  background: var(--zl-line);
  border: 1px solid var(--zl-line);
}

.layer-grid > div {
  display: grid;
  gap: .5rem;
  align-content: start;
  padding: clamp(1.25rem, 2.4vw, 1.85rem);
  background: var(--zl-surface);
}

.layer-grid b {
  color: var(--zl-ink);
  font-family: var(--zl-font-mono);
  font-size: var(--zl-text-sm);
  letter-spacing: var(--zl-tracking-label);
}

.layer-grid h3 {
  font-size: var(--zl-type-card-heading-size);
  line-height: var(--zl-leading-card);
}

.layer-grid p {
  color: var(--zl-body);
  font-size: var(--zl-text-sm);
  line-height: var(--zl-leading-copy);
}

.layer-grid > div[data-accent="build"] b { color: var(--zl-cobalt); }
.layer-grid > div[data-accent="spec"] b { color: var(--zl-vermilion); }

/* --- Product list ------------------------------------------------------ */
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  border-top: 1px solid var(--zl-line);
  border-left: 1px solid var(--zl-line);
}

/* > * not > div: a card that links out is an <a>, and a div-only selector
   silently dropped its padding and borders. */
.product-grid > * {
  display: grid;
  gap: .55rem;
  align-content: start;
  padding: clamp(1.35rem, 2.5vw, 2rem);
  border-right: 1px solid var(--zl-line);
  border-bottom: 1px solid var(--zl-line);
}

.product-grid :is(h3) {
  font-size: var(--zl-type-card-heading-size);
  line-height: var(--zl-leading-card);
}

.product-grid p {
  color: var(--zl-body);
  font-size: var(--zl-text-sm);
  line-height: var(--zl-leading-copy);
}

.product-grid .product-kind {
  color: var(--zl-muted);
  font-family: var(--zl-font-mono);
  font-size: var(--zl-text-xs);
  letter-spacing: var(--zl-tracking-label);
  text-transform: uppercase;
}

.stat-strip--three { grid-template-columns: 1fr; }

@media (min-width: 48rem) {
  .stat-strip { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .stat-strip--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* --- Grids with a fixed item count -------------------------------------
   auto-fit picks whatever fits, which orphans the last card whenever the
   count is not divisible by the chosen column number -- 4 items in 3 columns
   leaves one alone on row two, 9 items in 2 columns leaves one alone on row
   five. These three grids have known counts, so the counts are declared
   instead of discovered: only divisors are ever used. */
.family-grid--pair { grid-template-columns: 1fr; }          /* 2 items */
.family-grid--trio { grid-template-columns: 1fr; }          /* 3 items */
.family-grid--quad { grid-template-columns: 1fr; }          /* 4 items */
.family-grid--six { grid-template-columns: 1fr; }           /* 6 items */
.product-grid { grid-template-columns: 1fr; }               /* 9 items */

@media (min-width: 34rem) {
  .family-grid--pair,
  .family-grid--quad,
  .family-grid--six { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 60rem) {
  .family-grid--quad { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .family-grid--trio,
  .family-grid--six { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* Ten products, so the column count must be a divisor of ten or the last row
     strands a card — a hard 3 gave 3/3/3/1, and auto-fit gave 5/5 at 1400px but
     3/3/3/1 at 1000px, because it packs whatever fits rather than whatever
     divides [Arjun, 2026-08-07]. Declared 5 and 2 instead; every width lands on
     even rows. Revisit the counts when the product list changes length. */
  .product-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 70rem) {
  .product-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* ======================================================================
   Polish layer — animated icons, USP chips, animated provenance flow
   Everything here is gated on [data-reveal-state="in"] so nothing animates
   off-screen, the same discipline .solution-proof uses in scaffold.css.
   ====================================================================== */

/* --- Card icons --------------------------------------------------------
   Line icons that draw themselves in when their card reveals. The dash
   length is deliberately larger than any path in the set, so one rule
   covers every icon without per-path tuning. */
.card-icon {
  display: block;
  width: 1.75rem;
  height: 1.75rem;
  color: var(--zl-muted);
  overflow: visible;
}

.card-icon :is(path, circle, rect, line, polyline, polygon) {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

[data-reveal-ready] .card-icon :is(path, circle, rect, line, polyline, polygon) {
  stroke-dasharray: 160;
  stroke-dashoffset: 160;
}

[data-reveal-ready] [data-reveal-state="in"] .card-icon :is(path, circle, rect, line, polyline, polygon) {
  animation: icon-draw 1s var(--zl-ease-standard) forwards;
  animation-delay: calc(var(--reveal-index, 0) * 90ms + 160ms);
}

@keyframes icon-draw { to { stroke-dashoffset: 0; } }

/* Accent the icon on the cards that carry the argument. */
.layer-grid > div[data-accent="spec"] .card-icon,
.product-grid > div[data-accent="spec"] .card-icon { color: var(--zl-vermilion); }
.layer-grid > div[data-accent="build"] .card-icon,
.product-grid > div[data-accent="build"] .card-icon { color: var(--zl-cobalt); }

/* Signature motion, layered on after the draw finishes. Each says something
   about the product it sits on rather than being generic decoration. */
[data-reveal-state="in"] .card-icon--pulse { animation: icon-pulse 2.6s var(--zl-ease-in-out) 1.2s infinite; }
[data-reveal-state="in"] .card-icon--blink .icon-pupil { animation: icon-blink 3.4s var(--zl-ease-standard) 1.4s infinite; }
[data-reveal-state="in"] .card-icon--spin .icon-orbit { animation: icon-spin 6s linear 1.2s infinite; transform-origin: 12px 12px; }
[data-reveal-state="in"] .card-icon--drift .icon-drift { animation: icon-drift 3.2s var(--zl-ease-in-out) 1.2s infinite; }

@keyframes icon-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.07); } }
@keyframes icon-blink { 0%,92%,100% { opacity: 1; } 96% { opacity: .15; } }
@keyframes icon-spin { to { transform: rotate(360deg); } }
@keyframes icon-drift { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-2.5px); } }

/* --- USP chip ----------------------------------------------------------
   One per card at most. It names the single property that card is chosen
   for -- "Real-time", "Free" -- so a scanner gets the argument without
   reading the paragraph. */
.zl-chip {
  justify-self: start;
  padding: .2rem .5rem;
  border: 1px solid currentColor;
  border-radius: 2px;
  color: var(--zl-muted);
  font-family: var(--zl-font-mono);
  font-size: var(--zl-text-xs);
  letter-spacing: var(--zl-tracking-label);
  text-transform: uppercase;
  line-height: 1.4;
  /* One line: a wrapped chip makes its card's head row taller than its
     neighbours', and the row is what aligns the grid across cards. */
  white-space: nowrap;
}

.zl-chip[data-tone="build"] { color: var(--zl-cobalt); }
.zl-chip[data-tone="spec"] { color: var(--zl-vermilion); }

/* Cards carrying an icon and a chip lay out as: icon / chip / title / copy */
.layer-grid > div,
.product-grid > div,
.family-card { align-content: start; }

/* --- Animated provenance flow -----------------------------------------
   A line that fills left-to-right across the four steps as the section
   reveals, so the sequence reads as a sequence. Horizontal only: stacked,
   the steps already read top-to-bottom. */
@media (min-width: 60rem) {
  .flow-steps { position: relative; }

  .flow-steps::before {
    position: absolute;
    top: -1px;
    left: 0;
    height: 2px;
    width: 100%;
    background: var(--zl-line);
    content: "";
  }

  .flow-steps::after {
    position: absolute;
    top: -1px;
    left: 0;
    height: 2px;
    width: 100%;
    background: var(--zl-cobalt);
    transform: scaleX(0);
    transform-origin: 0 50%;
    content: "";
  }

  .flow-steps[data-reveal-state="in"]::after,
  [data-reveal-state="in"] .flow-steps::after {
    animation: flow-fill 1.8s var(--zl-ease-standard) .3s forwards;
  }
}

@keyframes flow-fill { to { transform: scaleX(1); } }

/* --- Stat strip: let the numbers land ---------------------------------- */
[data-reveal-ready] [data-reveal-state="in"] .stat-strip b { animation: stat-rise .7s var(--zl-ease-standard) both; }
@keyframes stat-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  [data-reveal-ready] .card-icon :is(path, circle, rect, line, polyline, polygon) {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    animation: none;
  }
  .card-icon--pulse, .card-icon--blink .icon-pupil,
  .card-icon--spin .icon-orbit, .card-icon--drift .icon-drift { animation: none !important; }
  .flow-steps::after { transform: scaleX(1); animation: none; }
  .stat-strip b { animation: none; }
}


/* --- Card head row -----------------------------------------------------
   Icon, figure and chip share one line instead of stacking. The layer cards
   were running five rows deep (count / icon / title / chip / copy), which
   read as a list of unrelated facts rather than one card. Three rows:
   head, title, copy. */
.card-head {
  display: flex;
  align-items: center;
  gap: .6rem;
  min-height: 1.75rem;
}

.card-head .card-icon { flex: 0 0 auto; }
.card-head > b,
.card-head > .product-kind { flex: 0 0 auto; margin: 0; }
.card-head .zl-chip { margin-left: auto; }

/* The figure sits with its icon, so it no longer needs its own row. */
.layer-grid .card-head b {
  color: var(--zl-ink);
  font-family: var(--zl-font-mono);
  font-size: var(--zl-text-sm);
  letter-spacing: var(--zl-tracking-label);
}
.layer-grid > div[data-accent="build"] .card-head b { color: var(--zl-cobalt); }
.layer-grid > div[data-accent="spec"] .card-head b { color: var(--zl-vermilion); }

/* At narrow widths a long chip would squeeze the figure, so it drops. */
@media (max-width: 34rem) {
  .card-head { flex-wrap: wrap; }
  .card-head .zl-chip { margin-left: 0; }
}

/* The step number moves into the head row beside its icon, so all four steps
   align on one baseline. Previously only Refresh carried an icon, which
   pushed its title a row lower than its neighbours'. */
.flow-steps { counter-reset: none; }
.flow-steps li::before { content: none; }

.flow-num {
  color: var(--zl-muted-text);
  font-family: var(--zl-font-mono);
  font-size: var(--zl-text-xs);
  letter-spacing: var(--zl-tracking-label);
}

/* --- Scale band --------------------------------------------------------
   Sits flush under the hero with no divider and no section padding, so the
   numbers read as the hero's own footing rather than a separate section.
   Full-bleed to the frame: the frame supplies the outer edges, so the strip
   drops its own left border and the last cell drops its right one --
   otherwise every edge is drawn twice and reads a shade darker. */
.stat-band { padding: 0; }

.stat-strip {
  border-top: 1px solid var(--zl-line);
  border-left: 0;
}

.stat-strip > div {
  border-bottom: 0;
  padding: clamp(1.35rem, 2.8vw, 2.1rem) clamp(1.25rem, 3.4vw, 4rem);
}

/* Two-up: the right-hand cell has the frame beside it, and the first row
   needs a rule under it that the four-up layout does not. */
@media (max-width: 47.99rem) {
  .stat-strip > div:nth-child(2n) { border-right: 0; }
  .stat-strip > div:nth-child(-n+2) { border-bottom: 1px solid var(--zl-line); }
}

@media (min-width: 48rem) {
  .stat-strip > div:last-child { border-right: 0; }
}

/* The stat band is full-bleed and its cells carry vertical rules, so the
   divider below it has to close that edge. Every other divider follows a
   padded section with no bottom rule of its own, which is why the base
   component only draws a bottom border. */
/* .section-divider already draws both rules full-bleed, so the band needs no
   extra border -- keeping one here would double the line. */

/* Four-segment layer grid: never three across, which strands the fourth card
   alone on a second row. 1 / 2 / 4 only -- all divisors of four. */
.layer-grid--quad { grid-template-columns: 1fr; }
@media (min-width: 34rem) { .layer-grid--quad { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 72rem) { .layer-grid--quad { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* Cards that are themselves links: same box, with the affordance a link needs. */
a.family-card { text-decoration: none; color: inherit; transition: background var(--zl-duration-fast) var(--zl-ease-standard); }
a.family-card:hover, a.family-card:focus-visible { background: var(--zl-bg); }


/* Anchor targets sit under a 72px sticky bar, so a jump would land with the
   heading hidden behind it. legal.css already does this for its sections;
   this generalises it to any section a link can point at. */
.zl-frame section[id] { scroll-margin-top: calc(var(--zl-nav-height) + 1.5rem); }

/* Eight-card grid: 1 / 2 / 4 -- divisors of eight, so the last row is never
   short. Same rule as the quad and six variants. */
.family-grid--eight { grid-template-columns: 1fr; }
@media (min-width: 34rem) { .family-grid--eight { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 60rem) { .family-grid--eight { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* A chip is nowrap so head rows stay aligned, which means an over-long one
   would escape its card. Clip at the card edge rather than overflow. */
.card-head { overflow: hidden; }

/* The landing's case grid is a fixed three columns for six cards. The Build
   page carries eight, so it declares divisors of eight instead. */
.build-cases__grid--eight { grid-template-columns: 1fr; }
@media (min-width: 34rem) { .build-cases__grid--eight { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Four across only once a column can actually hold a panel. The devices are
   capped at 18.5rem of content, so a four-column layout below ~1536px squeezed
   them and clipped rows off the top and bottom. Eight cards means 1/2/4 are
   the only options -- three would orphan two. */
@media (min-width: 96rem) { .build-cases__grid--eight { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* The wrapper only exists to carry .is-visible for the panel reveal, so it
   drops the surface and top rule it wears on the landing, where it is a
   standalone band rather than a section's contents. */
.zl-section > .build-cases { border-top: 0; background: none; }

/* Uniform panel height so every title in a row starts on the same line.
   The landing gets this free with three columns of similar devices; at four
   columns the tallest device would otherwise push its neighbours' text down. */
.build-cases__grid--eight .build-cases__panel { min-height: 0; height: clamp(12.5rem, 15vw, 15rem); }

/* Full-bleed case grid: the panels are the section's content, so they run to
   the frame edges the way the scale band does. Only the grid bleeds — the
   head keeps the section's gutter so the heading still aligns with every
   other heading on the page. The negative margin mirrors .zl-section's own
   padding-inline; below 40rem base.css zeroes that padding, so the offset
   goes with it. */
.zl-section > .build-cases {
  margin-inline: calc(-1 * clamp(1.25rem, 3.4vw, 4rem));
}

@media (max-width: 40rem) {
  .zl-section > .build-cases { margin-inline: 0; }
}

/* The frame draws the outer edges, so the grid drops the ones that would
   double up against them. */
.build-cases__grid--eight > article:nth-child(4n) { border-right: 0; }
.build-cases__grid--eight > article:nth-last-child(-n+4) { border-bottom: 0; }

@media (max-width: 96rem) {
  .build-cases__grid--eight > article:nth-child(4n) { border-right: 1px solid var(--zl-line); }
  .build-cases__grid--eight > article:nth-child(2n) { border-right: 0; }
  .build-cases__grid--eight > article:nth-last-child(-n+4) { border-bottom: 1px solid var(--zl-line); }
  .build-cases__grid--eight > article:nth-last-child(-n+2) { border-bottom: 0; }
}

@media (max-width: 34rem) {
  .build-cases__grid--eight > article { border-right: 0; }
  .build-cases__grid--eight > article:last-child { border-bottom: 0; }
}

/* A product card that is a link keeps the card box and gains the affordance. */
.product-grid a { text-decoration: none; color: inherit; transition: background var(--zl-duration-fast) var(--zl-ease-standard); }
.product-grid a:hover, .product-grid a:focus-visible { background: var(--zl-bg); }


/* Inline links in body copy: underlined, cobalt, so they read as links
   without borrowing the button styling. */
.zl-section p a, .family-card p a { color: var(--zl-cobalt); text-decoration: underline; text-underline-offset: 2px; }
.zl-section p a:hover { text-decoration-thickness: 2px; }

/* ======================================================================
   Linked cards
   A card that navigates is clickable across its whole box, carries a corner
   marker so that is visible before the pointer arrives, and opens in a new
   tab — so a reader comparing products does not lose the page they were on.
   ====================================================================== */
a.family-card,
.product-grid > a {
  position: relative;
  text-decoration: none;
  color: inherit;
  transition:
    background var(--zl-duration-fast) var(--zl-ease-standard),
    box-shadow var(--zl-duration-fast) var(--zl-ease-standard);
}

/* Corner marker: an outbound arrow, drawn rather than typed so it does not
   inherit the heading's font metrics or get read aloud. */
a.family-card::after,
.product-grid > a::after {
  position: absolute;
  top: clamp(1.1rem, 2vw, 1.5rem);
  right: clamp(1.1rem, 2vw, 1.5rem);
  width: .85rem;
  height: .85rem;
  background: currentColor;
  color: var(--zl-muted);
  content: "";
  -webkit-mask: var(--zl-arrow-out) center / contain no-repeat;
  mask: var(--zl-arrow-out) center / contain no-repeat;
  transition:
    transform var(--zl-duration-fast) var(--zl-ease-standard),
    color var(--zl-duration-fast) var(--zl-ease-standard);
}

a.family-card:hover,
a.family-card:focus-visible,
.product-grid > a:hover,
.product-grid > a:focus-visible {
  background: var(--zl-bg);
  box-shadow: inset 0 2px 0 var(--zl-cobalt);
}

a.family-card:hover::after,
a.family-card:focus-visible::after,
.product-grid > a:hover::after,
.product-grid > a:focus-visible::after {
  color: var(--zl-cobalt);
  transform: translate(2px, -2px);
}

/* The title reads as ordinary card text until the pointer arrives — colouring
   it cobalt at rest made every linked card shout before it was hovered. The
   corner arrow is the at-rest affordance; colour is the hover response. */
a.family-card h3,
.product-grid > a h3 {
  padding-right: 1.4rem;
  transition: color var(--zl-duration-fast) var(--zl-ease-standard);
}

a.family-card:hover h3,
a.family-card:focus-visible h3,
.product-grid > a:hover h3,
.product-grid > a:focus-visible h3 { color: var(--zl-cobalt); }

@media (prefers-reduced-motion: reduce) {
  a.family-card::after, .product-grid > a::after { transition: none; }
  a.family-card:hover::after, .product-grid > a:hover::after { transform: none; }
}

/* Three-panel case grid (the DM page's three questions).
   Borders are restated at :nth-child(n) rather than left to the base rule:
   build-section.css carries a max-width:1280px block for the landing dropping
   to TWO columns, and its :nth-child(2n) rule (specificity 0,2,1) outranks a
   plain child selector — so it was silently killing the rule between panels 2
   and 3 here. Matching the specificity and loading later wins it back. */
.build-cases__grid--three { grid-template-columns: 1fr; }

@media (min-width: 48rem) {
  .build-cases__grid--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .build-cases__grid--three > article:nth-child(n) {
    border-right: 1px solid var(--zl-line);
    border-bottom: 0;
  }
  .build-cases__grid--three > article:last-child { border-right: 0; }
}

@media (max-width: 47.99rem) {
  .build-cases__grid--three > article:nth-child(n) {
    border-right: 0;
    border-bottom: 1px solid var(--zl-line);
  }
  .build-cases__grid--three > article:last-child { border-bottom: 0; }
}

.build-cases__grid--three .build-cases__panel { min-height: 0; height: clamp(12.5rem, 15vw, 15rem); }

/* ======================================================================
   Compliance strip — the short security section every product page carries.
   The four marks are lifted verbatim from the landing's trust band (including
   the drawn DPDP lockup, which is easy to get wrong by hand); only the layout
   around them is compact, because a product page wants a reassurance strip
   rather than the landing's full band.
   ====================================================================== */
.trust-strip {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
  align-items: center;
}

@media (min-width: 56rem) {
  .trust-strip { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); }
}

.trust-strip__copy { display: grid; gap: var(--zl-space-3); align-content: start; justify-items: start; }
.trust-strip__copy h2 { font-size: var(--zl-type-section-heading-size, 1.75rem); line-height: var(--zl-leading-card); }
.trust-strip__copy p { color: var(--zl-body); font-size: var(--zl-text-sm); line-height: var(--zl-leading-copy); }

/* Four across, not the landing's two — the strip is wide and short. */
.trust-strip .trust-band__marks {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(.75rem, 2vw, 1.75rem);
  padding: clamp(1.1rem, 2.4vw, 1.75rem);
  border: 1px solid var(--zl-line);
  background: var(--zl-surface);
}

@media (max-width: 34rem) {
  .trust-strip .trust-band__marks { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Six-panel case grid. Borders restated at :nth-child(n) for the same reason
   as --three: build-section.css's max-width:1280px two-column rules outrank a
   plain child selector and would zero the wrong edges. */
.build-cases__grid--six { grid-template-columns: 1fr; }

@media (min-width: 48rem) {
  .build-cases__grid--six { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .build-cases__grid--six > article:nth-child(n) { border-right: 1px solid var(--zl-line); border-bottom: 1px solid var(--zl-line); }
  .build-cases__grid--six > article:nth-child(3n) { border-right: 0; }
  .build-cases__grid--six > article:nth-last-child(-n+3) { border-bottom: 0; }
}

@media (max-width: 47.99rem) {
  .build-cases__grid--six > article:nth-child(n) { border-right: 0; border-bottom: 1px solid var(--zl-line); }
  .build-cases__grid--six > article:last-child { border-bottom: 0; }
}

.build-cases__grid--six .build-cases__panel { min-height: 0; height: clamp(12.5rem, 15vw, 15rem); }

/* An explicit 1fr row, not align-content:stretch — the panel's rows are
   implicit and stayed content-sized, so the device never filled and its
   caption floated to wherever its own content ended. A declared track fills
   the panel and the caption pins to a shared baseline. */
.build-cases__grid--six .build-cases__panel {
  align-content: stretch;
  grid-template-rows: minmax(0, 1fr);
}

/* Two-panel case grid (the enrichment page's two input modes). */
.build-cases__grid--two { grid-template-columns: 1fr; }
@media (min-width: 48rem) {
  .build-cases__grid--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .build-cases__grid--two > article:nth-child(n) { border-right: 1px solid var(--zl-line); border-bottom: 0; }
  .build-cases__grid--two > article:last-child { border-right: 0; }
}
@media (max-width: 47.99rem) {
  .build-cases__grid--two > article:nth-child(n) { border-right: 0; border-bottom: 1px solid var(--zl-line); }
  .build-cases__grid--two > article:last-child { border-bottom: 0; }
}
.build-cases__grid--two .build-cases__panel { min-height: 0; height: clamp(15rem, 19vw, 18rem); align-content: stretch; }
.build-cases__grid--two .build-cases__panel,
.build-cases__grid--six .build-cases__panel { grid-template-rows: minmax(0, 1fr); }


/* --- The call: request → response ---------------------------------------
   Its own section, not a block inside the four steps. Those steps are a
   conceptual narrative for any reader; this is a concrete sample for a
   developer. Sharing a row forced the sample into a quarter-width card where
   every token wrapped onto its own line, which read as broken output rather
   than as code. Given a section, each side gets half the frame and the
   arrow between them carries the relationship the steps used to imply. */
.callpair {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  align-items: stretch;
  border: 1px solid var(--zl-line);
  background: var(--zl-surface);
}

@media (min-width: 52rem) {
  .callpair { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
}

.callpair__block { display: grid; gap: 0; align-content: start; min-width: 0; }

/* A request that is a fraction of its response leaves the shorter column
   mostly empty, because .callpair stretches both to the same height. The Grow
   enrichment call is four lines against thirty-five [Arjun, 2026-08-09], so
   its request is centred against the response instead of sitting at the top
   of a tall void. The heading stays pinned; only the code moves. */
.callpair--lopsided .callpair__block:first-child { align-content: stretch; grid-template-rows: auto 1fr; }
.callpair--lopsided .callpair__block:first-child .callpair__code { align-self: center; }

.callpair__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin: 0;
  padding: .7rem clamp(1rem, 2vw, 1.35rem);
  border-bottom: 1px solid var(--zl-line);
  color: var(--zl-muted-text);
  font-family: var(--zl-font-mono);
  font-size: var(--zl-text-xs);
  letter-spacing: var(--zl-tracking-label);
  text-transform: uppercase;
}
.callpair__head b { color: var(--zl-cobalt); font-weight: var(--zl-weight-medium); }

.callpair__code {
  margin: 0;
  padding: clamp(1rem, 2vw, 1.35rem);
  color: var(--zl-ink);
  font-family: var(--zl-font-mono);
  font-size: .74rem;
  line-height: 1.7;
  /* Scroll, never wrap — a broken line in a sample reads as a mistake. */
  white-space: pre;
  overflow-x: auto;
}
.callpair__code i { color: var(--zl-muted-text); font-style: normal; }
.callpair__code b { color: var(--zl-cobalt); font-weight: 400; }

/* The connector. A column of its own on wide screens, a rule between the two
   stacked blocks below that. */
.callpair__link {
  display: grid;
  place-items: center;
  padding: .75rem;
  border-block: 1px solid var(--zl-line);
  color: var(--zl-cobalt);
}
.callpair__link svg { width: 1.1rem; height: 1.1rem; transform: rotate(90deg); }

@media (min-width: 52rem) {
  .callpair__link { border-block: 0; border-inline: 1px solid var(--zl-line); }
  .callpair__link svg { transform: none; }
}
