/* ============================================================
   WILDSTREAM — Reusable composition components
   Built only from tokens + .ws-* / .mk- atoms in
   colors_and_type.css + marketing.css. Portable across pages:
   no parent-scoped selectors, no page-specific dependencies
   (host-tweak vars carry fallbacks so a component is self-sufficient).
   ============================================================ */

/* ---------- Section eyebrow ----------
   The single eyebrow construction. Type + color come from .ws-meta;
   this owns only the short line marker. Pair: <span class="section__eyebrow ws-meta">. */
.section__eyebrow { display: inline-flex; align-items: center; gap: var(--space-3); }
.section__eyebrow::before { content: ""; width: 22px; height: 1px; background: currentColor; opacity: 0.5; }
/* ---------- Breadcrumb eyebrow ----------
   The single breadcrumb construction for secondary pages: reads as one eyebrow
   line — leading line marker, parent link(s), hairline separator (same line as
   the eyebrow marker), current page name. Type + color come from .ws-meta;
   parent links use .ws-link. Pair:
   <nav class="ws-breadcrumb ws-meta" aria-label="Breadcrumb">
     <a class="ws-link" href="…">Parent</a>
     <span class="ws-breadcrumb__sep"></span>
     <span aria-current="page">Current Page</span>
   </nav> */
.ws-breadcrumb { display: inline-flex; align-items: center; gap: var(--space-3); }
.ws-breadcrumb::before { content: ""; width: 22px; height: 1px; background: currentColor; opacity: 0.5; }
.ws-breadcrumb__sep { flex: none; width: 22px; height: 1px; background: currentColor; opacity: 0.5; }

/* ---------- Text link ----------
   The single plain text-link construction. Muted at rest, full-contrast on
   hover, no underline. Owns only link color + hover; pair with a type preset
   for scale, e.g. <a class="ws-link ws-meta">&larr; Industries</a>. */
.ws-link { color: var(--fg-muted); text-decoration: none; transition: color var(--dur-fast) var(--ease-out-soft); }
.ws-link:hover { color: var(--fg); }

/* ---------- Card stack ----------
   Vertical stack of cards with the standard card gap. Used e.g. for the
   case-study cards inside a line card. */
.ws-card-stack { display: grid; gap: var(--space-5); }

/* eyebrow sits above the title in a .mk-section-head with consistent spacing */
.mk-section-head__l .section__eyebrow { margin-bottom: var(--space-5); white-space: nowrap; }
/* On a dark scope, lift the eyebrow to a light purple for contrast against the
   surface (the ::before marker follows via currentColor). Covers both the DS
   dark token scope and the self-dark surface components (.ws-panel-glow). */
.ws-dark .section__eyebrow,
[data-theme="dark"] .section__eyebrow,
.ws-panel-glow .section__eyebrow,
.ws-dark .ws-breadcrumb,
[data-theme="dark"] .ws-breadcrumb,
.ws-panel-glow .ws-breadcrumb { color: var(--ws-purple-200); }
/* title blocks share one construction — eyebrow margin is the only spacing (reset UA h2 margin) */
.mk-section-head__l .ws-h2 { margin: 0; }

/* ============================================================
   LINE CARD  (.ws-card-line-grid / .ws-card-line)
   Title on top, body below, a rule (line) at the top of each card.
   Default is 3 per row. Control the column count with a modifier:
     .ws-card-line-grid--2 / --3 / --4  (2 is the minimum, 4 the maximum).
   All variants collapse responsively (2-up on medium, 1-up on small).
   ============================================================ */
.ws-card-line-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-16) var(--grid-gutter);
  background: var(--bg);
  --mk-stagger: 160ms; /* cards reveal one-by-one, a touch slower than the global cadence */
}
.ws-card-line {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding-top: var(--space-6);
  padding-bottom: var(--space-6);
  border-top: 1px solid var(--border-strong);
}
.ws-card-line__title { margin: 0; }
.ws-card-line__title-em { display: block; font-weight: var(--fw-bold); }
.ws-card-line__body { margin: 0; }
/* extra breathing room between title and body */
.ws-card-line__title + .ws-card-line__body { margin-top: var(--space-3); }
/* Column-count variants. Min 2, max 4. Placed before the responsive
   queries below so those win and collapse every variant on smaller screens. */
/* Surface opt-out: let the host surface (e.g. a sunken panel tint) show through */
.ws-card-line-grid--transparent { background: transparent; }
.ws-card-line-grid--2 { grid-template-columns: repeat(2, 1fr); }
.ws-card-line-grid--3 { grid-template-columns: repeat(3, 1fr); }
.ws-card-line-grid--4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 860px) {
  .ws-card-line-grid { grid-template-columns: 1fr; gap: var(--space-8) 0; }
}
@media (min-width: 861px) and (max-width: 1100px) {
  .ws-card-line-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ============================================================
   AURORA CARD  (.ws-card-aurora-grid / .ws-card-aurora)
   Filled, rounded card whose surface IS the generic aurora background (light
   purple gradient + grain + drifting tan blobs, with motion) — no border/line,
   just the background. The surface + motion come from the generic aurora
   background classes (.mk-panel--aurora + .mk-aurora, in marketing.css), so any
   change to that background propagates everywhere it is used. This card owns
   only layout: the 2-up grid, radius, padding, column stacking, and lifting
   content above the grain overlay. Self-contained — no dependency on any other
   card. Defined for the Industries page for now; broader usage TBD. Markup:
     <div class="ws-card-aurora-grid">
       <article class="ws-card-aurora mk-panel--aurora">
         <div class="mk-aurora" aria-hidden="true"></div>
         <h3 class="ws-card-aurora__title ws-h3">…</h3>
         <p  class="ws-card-aurora__body ws-body">…</p>
         <a  class="mk-btn mk-btn--secondary ws-card-aurora__cta" href="…">View details <span class="arrow">→</span></a>
       </article>
     </div>
   ============================================================ */
.ws-card-aurora-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-8) var(--grid-gutter);
  --mk-stagger: 160ms;
}
.ws-card-aurora {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border-radius: var(--radius-md);
  padding: clamp(var(--space-6), 2.4vw, var(--space-8));
}
/* (content-lift above grain is generic on .mk-panel--aurora, in marketing.css) */
/* signature icon slot inside the aurora card (Engineered Line, icon tier) */
.ws-card-aurora__viz {
  --guide: color-mix(in oklab, var(--ws-tan-400), var(--ws-tan-500));
  margin-bottom: var(--space-6);
  display: none; /* hidden for now — markup kept; re-enable to bring icons back */
}
.ws-card-aurora__viz svg { display: block; width: 88px; height: 88px; }
.ws-card-aurora__title { margin: 0; }
.ws-card-aurora__body { margin: 0; }
.ws-card-aurora__title + .ws-card-aurora__body { margin-top: var(--space-12); }
.ws-card-aurora__cta { align-self: flex-start; margin-top: var(--space-4); }
@media (max-width: 860px) {
  .ws-card-aurora-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-6) 0; }
}

/* ============================================================
   LIST CARD  (.ws-card-list / .ws-card-list__row)
   A 12-col ledger: animated arrow marker | title | body, hairline rows.
   To use: wrap items in .ws-card-list and give each row .ws-card-list__row with
   .ws-card-list__index (arrow svg) / .ws-card-list__title / .ws-card-list__body.
   ============================================================ */
.ws-card-list { border-top: 1px solid var(--border); }
.ws-card-list__row {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--grid-gutter);
  align-items: start;
  padding: var(--space-10) 0;
  border-bottom: 1px solid var(--border);
}
.ws-card-list__index { grid-column: 1 / span 1; }
.ws-card-list__title { grid-column: 2 / span 4; }
.ws-card-list__body  { grid-column: 7 / span 6; }
/* arrow marker — DS Lucide arrow-right, accent colour.
   On hover the shaft lengthens and the head slides right; neither changes size. */
.ws-card-list__index {
  align-self: start;
  /* match the title's first-line box and center the marker within it, so the
     arrow aligns to the title's first line instead of floating below it */
  height: 1.35em; /* = .ws-h5 line-height at the shared base size */
  align-items: center;
  color: var(--accent);
  display: flex;
}
.ws-card-list__index svg { width: 32px; height: 24px; display: block; overflow: visible; }
.ws-card-list__arrow-shaft,
.ws-card-list__arrow-head {
  transform-box: fill-box;
  transition: transform var(--dur-base) var(--ease-out-soft);
}
.ws-card-list__arrow-shaft { transform-origin: left center; }
.ws-card-list__arrow-head { transform-origin: center; }
.ws-card-list__row:hover .ws-card-list__arrow-shaft { transform: scaleX(1.8); }
.ws-card-list__row:hover .ws-card-list__arrow-head { transform: translateX(224%); }
.ws-card-list__title { margin: 0; }
.ws-card-list__body { margin: 0; }
@media (max-width: 860px) {
  .ws-card-list__row { gap: var(--space-3) var(--grid-gutter); padding: var(--space-8) 0; }
  .ws-card-list__index { grid-column: 1 / -1; }
  .ws-card-list__title { grid-column: 1 / -1; }
  .ws-card-list__body  { grid-column: 1 / -1; }
}

/* ============================================================
   LIST-IMAGE CARD  (.ws-card-list-image)
   Sibling of .ws-card-list: a single full-width block sharing the same
   top hairline. Below the hairline a 12-col row splits the image (left 6
   columns) and the content (right 6 columns); the title sits inside the
   content column, above the body text. The row is image (5 cols) · one-column
   gap · content (6 cols). Media reuses .ws-card-media__media--illo
   for the illo canvas. Collapses to 1-up on small screens.
   ============================================================ */
.ws-card-list-image { border-top: 1px solid var(--border); padding-top: var(--space-8); }
.ws-card-list-image__title { margin: 0; }
.ws-card-list-image__row {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--grid-gutter);
  align-items: start;
}
.ws-card-list-image__media { grid-column: 1 / span 5; grid-row: 1; align-self: start; }
.ws-card-list-image__body  { grid-column: 7 / span 6; grid-row: 1; }
.ws-card-list-image__body > * { margin: 0; }
.ws-card-list-image__body > * + * { margin-top: var(--space-4); }
@media (max-width: 860px) {
  .ws-card-list-image__row { grid-template-columns: 1fr; gap: var(--space-6); }
  .ws-card-list-image__media { grid-column: 1 / -1; grid-row: auto; }
  .ws-card-list-image__body  { grid-column: 1 / -1; grid-row: auto; }
}
/* List of list-image cards. Rows alternate sides automatically: odd rows keep
   image-left, even rows mirror to image-right (media cols 8-12, body cols 1-6).
   Spacing between rows is owned here, not by per-item inline margins. */
.ws-card-list-image-list > .ws-card-list-image + .ws-card-list-image { margin-top: var(--space-12); }
.ws-card-list-image-list > .ws-card-list-image:nth-child(even) .ws-card-list-image__media { grid-column: 8 / span 5; }
.ws-card-list-image-list > .ws-card-list-image:nth-child(even) .ws-card-list-image__body  { grid-column: 1 / span 6; }
@media (max-width: 860px) {
  .ws-card-list-image-list > .ws-card-list-image:nth-child(even) .ws-card-list-image__media,
  .ws-card-list-image-list > .ws-card-list-image:nth-child(even) .ws-card-list-image__body { grid-column: 1 / -1; }
}

/* ============================================================
   SIGNATURE DIAGRAM  (.ws-sig-diagram)
   Process-flow: fan-in (dashed/neutral) → hub → fan-out (solid/accent).
   --sig-* theme hooks mapped to DS tokens. A "signature asset".
   ============================================================ */
.ws-sig-diagram {
  --sig-accent:        var(--ws-red);
  --sig-fg:            var(--fg);
  --sig-fg-muted:      var(--fg-muted);
  --sig-fg-subtle:     var(--fg-subtle);
  --sig-hub:           var(--ws-red);
  --sig-hub-label:     var(--ws-tan-50);
  --sig-surface:       var(--bg-elevated);
  --sig-card:          transparent;
  --sig-border:        var(--border);
  --sig-border-strong: var(--border-strong);
  --sig-border-faint:  var(--border-faint);
  --sig-grid:          color-mix(in oklab, var(--fg) 5%, transparent);

  position: relative;
  border: 0;
  border-radius: 0;
  padding: clamp(var(--space-6), 3vw, var(--space-10));
  background-image:
    linear-gradient(var(--sig-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--sig-grid) 1px, transparent 1px);
  background-size: 44px 44px;
  background-position: -1px -1px;
}
.ws-sig-diagram::before, .ws-sig-diagram::after {
  content: ""; position: absolute; width: 10px; height: 10px;
  border: 1px solid var(--sig-accent); pointer-events: none;
}
.ws-sig-diagram::before { top: 8px; left: 8px; border-right: 0; border-bottom: 0; }
.ws-sig-diagram::after  { bottom: 8px; right: 8px; border-left: 0; border-top: 0; }
.ws-sig-diagram__flow {
  display: grid; grid-template-columns: 1fr 80px 72px 80px 1fr;
  align-items: stretch; min-height: 260px;
  position: relative; z-index: 1;
}
.ws-sig-diagram__col { display: flex; flex-direction: column; justify-content: space-around; }
.ws-sig-diagram__col--from { align-items: flex-end; }
.ws-sig-diagram__col--to { align-items: flex-start; }
.ws-sig-diagram__node {
  font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: var(--tracking-eyebrow);
  padding: 7px 16px; border-radius: var(--radius-pill);
  border: 1px solid var(--sig-border); background: var(--sig-surface);
  display: inline-flex; align-items: center; width: fit-content; margin: 6px 0; white-space: nowrap;
}
.ws-sig-diagram__node--from { color: var(--sig-fg-muted); border-style: dashed; }
.ws-sig-diagram__node--to {
  color: var(--sig-fg); border-color: var(--sig-accent);
  background: transparent;
}
.ws-sig-diagram__connectors { display: flex; align-items: center; }
.ws-sig-diagram__svg { width: 100%; height: 100%; }
.ws-sig-diagram__svg path { stroke: var(--sig-border-strong); stroke-width: 1; fill: none; vector-effect: non-scaling-stroke; }
.ws-sig-diagram__connectors--out .ws-sig-diagram__svg path { stroke: color-mix(in oklab, var(--sig-accent) 72%, transparent); }
.ws-sig-diagram__center { display: flex; align-items: center; justify-content: center; }
.ws-sig-diagram__hub {
  width: 56px; height: 56px; border-radius: var(--radius-pill);
  background: var(--sig-hub); color: var(--sig-hub-label);
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-md); position: relative; z-index: 1;
}
.ws-sig-diagram__hub-label { font-family: var(--font-mono); font-size: var(--fs-xs); letter-spacing: var(--tracking-eyebrow); text-transform: uppercase; }
@media (max-width: 980px) {
  /* Mobile + tablet: keep the horizontal left → hub → right flow, shrunk to fit; the gap
     axis stays overlaid on the left as designed. Node labels may wrap to two
     lines. (Desktop unchanged.) */
  /* Maximize horizontal space on phones: shrink the diagram's own padding
     (16px → 8px) AND break it out of the host .mk-panel's 16px side padding
     via a negative inline margin, so the diagram spans the full card width and
     its edge lands on the 16px page gutter. Reclaims ~48px total for the flow.
     Every .ws-sig-diagram sits inside a .mk-panel, so the -16px is exact. */
  .ws-sig-diagram { padding: var(--space-2); margin-inline: calc(-1 * var(--space-4)); }
  /* Even (space-around) node distribution is what the fixed fan-connector curves
     are authored for — pill centres land on the curve anchor points (12.5 / 37.5
     / 62.5 / 87.5%), so the connectors point at the right pills. padding-block
     reserves the top/bottom zone for the axis markers (independent of height);
     min-height sizes only the pills+connectors band, kept compact. */
  .ws-sig-diagram__flow { grid-template-columns: 1fr 24px 40px 24px 1fr; min-height: 284px; gap: 4px; padding-block: var(--space-10); }
  .ws-sig-diagram__node {
    font-size: 9px; padding: 4px 8px; margin: 0;
    white-space: normal; text-align: center; line-height: 1.25; max-width: 100%;
  }
  .ws-sig-diagram__hub { width: 40px; height: 40px; box-shadow: var(--shadow-sm); }
  .ws-sig-diagram__hub-label { font-size: 8px; }
  /* Axis: keep only Ambition + Operational reality. Drop the middle red node
     and its "liability gap" label (the dashed spine between them stays). */
  .ws-gap-axis__node, .ws-gap-axis__label--gap { display: none; }
}

/* ---- Conclusion node: a single large highlighted outcome container on the
   --to side, used in place of multiple small pills when many conditions
   converge on one result. Pair with .ws-sig-diagram__col--single so the
   column centres and the panel fills its width. Reuses --sig-* tokens. */
.ws-sig-diagram__col--single { justify-content: center; align-items: stretch; }
.ws-sig-diagram__conclusion {
  border: 1px solid var(--sig-accent);
  border-radius: var(--radius-md);
  background: color-mix(in oklab, var(--sig-accent) 6%, var(--sig-surface));
  padding: var(--space-6);
  display: flex; flex-direction: column; gap: var(--space-3);
  box-shadow: var(--shadow-sm);
}
.ws-sig-diagram__conclusion-label { color: var(--sig-accent); }
.ws-sig-diagram__conclusion-body { margin: 0; color: var(--sig-fg); }

/* ============================================================
   GAP AXIS  (.ws-gap-axis)
   A vertical "gap" overlay for the left edge of a .ws-sig-diagram:
   a top marker (Ambition), a dashed rail with one red node (the gap),
   and a bottom marker (Operational readiness). Self-contained — drop it
   as the first child of any position:relative container (.ws-sig-diagram
   already is). Markers pair a coloured dot with a mono uppercase label.
   ============================================================ */
.ws-gap-axis {
  position: absolute; z-index: 2; pointer-events: none;
  left: clamp(var(--space-6), 3vw, var(--space-10));
  top: clamp(var(--space-6), 3vw, var(--space-10));
  bottom: clamp(var(--space-6), 3vw, var(--space-10));
  display: flex; flex-direction: column; align-items: flex-start;
}
.ws-gap-axis__mark { display: flex; align-items: center; gap: 10px; }
.ws-gap-axis__dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.ws-gap-axis__dot--amb { background: var(--ws-purple-300); }
.ws-gap-axis__dot--ops { background: var(--ws-purple-400); }
.ws-gap-axis__label {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--fg-muted); white-space: nowrap;
}
.ws-gap-axis__rail { flex: 1 1 auto; width: 9px; position: relative; display: flex; align-items: center; justify-content: center; }
.ws-gap-axis__rail::before {
  content: ""; position: absolute; top: 7px; bottom: 7px; left: 50%; transform: translateX(-50%);
  border-left: 1px dashed var(--border-strong);
}
.ws-gap-axis__node { width: 9px; height: 9px; border-radius: 50%; background: var(--ws-red); position: relative; z-index: 1; }
/* the gap label, beside the red node */
.ws-gap-axis__label--gap { position: absolute; left: 19px; top: 50%; transform: translateY(-50%); color: var(--ws-red); }
/* tan highlight band behind the liability-gap elements (axis rail + from-side pills);
   right edge tracks the first pill column + 20px via calc(50% + 96px) at any width */
.ws-gap-highlight {
  position: absolute; z-index: 0; pointer-events: none;
  left: calc(clamp(var(--space-6), 3vw, var(--space-10)) + 4px);
  right: calc(50% + 96px);
  top: calc(clamp(var(--space-6), 3vw, var(--space-10)) + 24px);
  bottom: calc(clamp(var(--space-6), 3vw, var(--space-10)) + 24px);
  background: color-mix(in oklab, var(--ws-tan-400) 24%, transparent);
  border-radius: var(--radius-md);
}
@media (max-width: 980px) { .ws-gap-highlight { display: none; } }

/* ============================================================
   GLOW PANEL  (.ws-panel-glow)  +  CONTRAST LIST  (.ws-list-contrast)
   Dark purple atmospheric panel (glow + grain) that holds a header,
   a contrast list ("struck-out vs kept"), and a closing line.
   The two are independent and can be used separately.
   ============================================================ */
.ws-panel-glow {
  background: var(--ws-purple);
  color: var(--fg-on-dark);
  border-radius: var(--radius-md);
  position: relative; overflow: hidden; isolation: isolate;
  padding: clamp(var(--space-10), 4vw, var(--space-20));
}
.ws-panel-glow__glow {
  position: absolute; inset: -15%;
  background:
    radial-gradient(45% 35% at 12% 88%, color-mix(in oklab, var(--ws-red) 70%, transparent), transparent 62%),
    radial-gradient(38% 30% at 88% 18%, color-mix(in oklab, var(--ws-red) 55%, transparent), transparent 60%),
    radial-gradient(60% 45% at 50% 50%, color-mix(in oklab, var(--ws-purple-700) 80%, transparent), transparent 70%);
  filter: blur(60px); pointer-events: none; z-index: 0;
  animation: panel-glow-drift 22s ease-in-out infinite alternate;
}
@keyframes panel-glow-drift {
  0%   { transform: translate(-3%, -2%) scale(1); }
  100% { transform: translate( 3%,  3%) scale(1.08); }
}
@media (prefers-reduced-motion: reduce) { .ws-panel-glow__glow { animation: none; } }
.ws-panel-glow__grain {
  position: absolute; inset: 0;
  background-image: var(--grain); background-size: 220px 220px;
  opacity: 0.35; mix-blend-mode: overlay; pointer-events: none; z-index: 1;
}
.ws-panel-glow__inner { position: relative; z-index: 2; }
.ws-panel-glow__head {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--grid-gutter);
  margin-bottom: var(--space-20);
}
.ws-panel-glow__head-l { grid-column: 1 / span 5; }
.ws-panel-glow__head-r { grid-column: 7 / span 6; align-self: start; }
/* lead TYPE (size/line-height, incl. mobile step-down) comes from the shared
   .ws-lead group in colors_and_type.css; this rule owns only layout + on-dark color. */
.ws-panel-glow__head-r p { margin: 0; color: color-mix(in oklab, var(--fg-on-dark) 78%, transparent); }
/* Stacked, hairline-separated process steps inside the glow head-r column
   (reuses .ws-card-line, on-dark styled below). */
.ws-panel-glow__steps { margin-top: var(--space-6); }
.ws-panel-glow__steps strong { color: var(--fg-on-dark); font-weight: var(--fw-bold); }
.ws-panel-glow__title { color: var(--fg-on-dark); margin-top: var(--space-5); }
.ws-list-contrast {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--grid-gutter);
  margin-bottom: var(--space-20);
}
.ws-list-contrast__viz { grid-column: 1 / span 5; grid-row: 1 / span 3; align-self: stretch; min-height: 300px; }
.ws-list-contrast__viz canvas { width: 100%; height: 100%; display: block; }
.ws-list-contrast__viz {
  -webkit-mask-image: radial-gradient(ellipse 80% 75% at 50% 50%, black 38%, transparent 78%);
  mask-image: radial-gradient(ellipse 80% 75% at 50% 50%, black 38%, transparent 78%);
}
.ws-list-contrast__item {
  grid-column: 7 / span 6;
  border-top: 1px solid color-mix(in oklab, var(--fg-on-dark) 18%, transparent);
  padding-top: var(--space-6);
}
.ws-list-contrast__strike {
  font-size: var(--fs-base);
  color: color-mix(in oklab, var(--fg-on-dark) 45%, transparent);
  text-decoration: line-through;
  text-decoration-color: color-mix(in oklab, var(--ws-red) 60%, transparent);
  text-decoration-thickness: 1.5px;
  margin: 0 0 var(--space-3);
}
.ws-list-contrast__line { margin: 0; color: var(--fg-on-dark); }
/* Contextual placement: when the breathe-grid viz sits in the glow-panel's left
   column (below the title), give it breathing room from the heading. */
.ws-panel-glow__head-l .ws-list-contrast__viz { margin-top: var(--space-8); }
/* Section close — a wrap-up line set off by a top hairline; children span full width.
   Shared by the value section and (with the on-dark override below) the glow panel. */
.ws-section-close {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--grid-gutter);
  padding-top: var(--space-12);
  border-top: 1px solid var(--border);
}
.ws-section-close > * { grid-column: 1 / -1; margin: 0; }
/* Contextual top spacing: only when the closing line follows a sibling (never as
   a first child). Same adjacency idiom as `* + .ws-list-marked` — keeps the
   component portable and removes the need for inline margin-top on host pages. */
* + .ws-section-close { margin-top: var(--space-12); }
/* glow-panel context: lighten the hairline for the dark surface; stack multiple leads */
.ws-panel-glow__close { border-top-color: color-mix(in oklab, var(--fg-on-dark) 14%, transparent); }
.ws-panel-glow__close-l { display: flex; flex-direction: column; gap: var(--space-5); }
/* type from .ws-lead; component owns on-dark color (matches .ws-panel-glow__head-r) */
.ws-panel-glow__close-l p {
  margin: 0; color: color-mix(in oklab, var(--fg-on-dark) 78%, transparent);
}
@media (max-width: 980px) {
  .ws-panel-glow__head-l, .ws-panel-glow__head-r, .ws-list-contrast__item { grid-column: 1 / -1; }
  /* hide the breathe-grid viz on mobile (homepage + industry panels) */
  .ws-list-contrast__viz { display: none; }
}
/* Line cards on the glow panel — adapt the light-surface .ws-card-line to the
   dark atmospheric surface: transparent grid (glow shows through), on-dark type,
   lightened hairline. Reusable wherever .ws-card-line sits inside .ws-panel-glow. */
.ws-panel-glow .ws-card-line-grid { background: transparent; }
.ws-panel-glow .ws-card-line { border-top-color: color-mix(in oklab, var(--fg-on-dark) 22%, transparent); }
.ws-panel-glow .ws-card-line__title { color: var(--fg-on-dark); }
.ws-panel-glow .ws-card-line__body { color: color-mix(in oklab, var(--fg-on-dark) 78%, transparent); }

/* ============================================================
   GET IN TOUCH  (.ws-contact)
   Contained dark banner on its own atmospheric background, with a
   collapsible inline form. Unscoped/portable — a host page positions
   it (e.g. margin-top) in its own layout context.
   Host-tweak vars (--d-mesh-blur / --d-grain-opacity) carry fallbacks.
   ============================================================ */
.ws-contact {
  position: relative;
  padding: clamp(var(--space-10), 4vw, var(--space-16));
  border-radius: var(--radius-md);
  overflow: hidden;
  isolation: isolate;
  background: var(--ws-purple-700);
  color: var(--fg-on-dark);
  box-shadow: var(--shadow-lg);
}
/* Compact variant — for a minimal CTA: a single-line heading + one button,
   no lede or prompt list. Gives the banner roomier padding, drops the heading's
   eyebrow-spacing top margin (no eyebrow here), and centers the action against
   the single heading line instead of bottom-aligning it. Apply on the root:
   <div class="ws-contact ws-contact--actions-small">. */
.ws-contact--actions-small { padding: clamp(var(--space-16), 5vw, var(--space-24)); }
.ws-contact--actions-small .ws-contact__h2 { margin-top: 0; }
.ws-contact--actions-small .ws-contact__actions { align-self: center; }
.ws-contact__glow {
  position: absolute; inset: -15%;
  pointer-events: none; z-index: 0;
  background:
    /* red core — listed first so it sits on top, concentric with the halo */
    radial-gradient(32% 30% at 74% 56%, color-mix(in oklab, var(--ws-red) 84%, transparent), transparent 66%),
    /* light purple halo — same center, larger radius */
    radial-gradient(72% 66% at 74% 56%, color-mix(in oklab, var(--ws-purple-300) 80%, transparent), transparent 78%),
    /* darkest purple — opposite corner for depth */
    radial-gradient(82% 74% at 14% 16%, color-mix(in oklab, var(--ws-purple-900) 84%, transparent), transparent 80%);
  filter: blur(calc(var(--d-mesh-blur, 40px) * 1.2));
  animation: contact-drift 20s ease-in-out infinite alternate;
}
@keyframes contact-drift {
  0%   { transform: translate(-3%, -2%) scale(1); }
  100% { transform: translate(3%, 3%) scale(1.08); }
}
@media (prefers-reduced-motion: reduce) { .ws-contact__glow { animation: none; } }
.ws-contact__grain {
  position: absolute; inset: 0;
  background-image: var(--grain); background-size: 220px 220px;
  opacity: var(--d-grain-opacity, 0.42); mix-blend-mode: overlay;
  pointer-events: none; z-index: 1;
}
.ws-contact__content { position: relative; z-index: 2; }
.ws-contact__h2 {
  margin: 24px 0 0;
  color: var(--fg-on-red); text-wrap: balance;
}
.ws-contact__lede {
  margin-top: 28px;
  color: color-mix(in oklab, var(--fg-on-red) 88%, transparent);
}
/* prompt list — composed from ws-body + the line-marker idiom (no DS list atom yet) */
.ws-contact__prompts {
  list-style: none; margin: var(--space-6) 0 0; padding: 0;
  display: grid; gap: var(--space-3);
}
.ws-contact__prompts li {
  display: flex; align-items: center; gap: var(--space-4);
  color: color-mix(in oklab, var(--fg-on-red) 82%, transparent);
}
.ws-contact__prompts li::before {
  content: ""; flex: none;
  width: 18px; height: 1px;
  background: color-mix(in oklab, var(--fg-on-red) 70%, transparent);
}
/* collapsible banner */
.ws-contact__bar { align-items: center; row-gap: 20px; }
.ws-contact__bar-text { grid-column: 1 / span 7; }
.ws-contact__actions { grid-column: 9 / span 4; display: flex; justify-content: center; align-items: center; align-self: end; }
/* appearance from .mk-btn; component owns only the collapse show/hide behavior */
.ws-contact__close { display: none; }
.ws-contact.is-open .ws-contact__toggle { display: none; }
.ws-contact.is-open .ws-contact__close { display: inline-flex; }
.ws-contact__expand-inner { overflow: hidden; max-height: 0; transition: max-height var(--dur-slow) var(--ease-out-soft); }
.ws-contact__expand-top { display: flex; justify-content: flex-end; padding-top: 12px; }
.ws-contact__expand .ws-contact__lede, .ws-contact__expand .form-inline {
  opacity: 0; transform: translateY(10px);
  transition: opacity var(--dur-base) var(--ease-out-soft), transform var(--dur-base) var(--ease-out-soft);
}
.ws-contact.is-open .ws-contact__expand .ws-contact__lede { opacity: 1; transform: none; transition-delay: 120ms; }
.ws-contact.is-open .ws-contact__expand .form-inline { opacity: 1; transform: none; transition-delay: 200ms; }

/* ---------- Info variant (.ws-contact--info) ----------
   Keeps the top bar (heading + lede) and hides the interactive actions/form
   (kept in the DOM so the collapse behavior stays wired and can be switched
   back on). Below a hairline, shows one contact block: office address, email
   and LinkedIn as real links. Pair: <div class="ws-contact ws-contact--info">. */
.ws-contact--info .ws-contact__actions,
.ws-contact--info .ws-contact__expand { display: none; }
.ws-contact__info {
  margin-top: var(--space-10);
  padding-top: var(--space-8);
  border-top: 1px solid color-mix(in oklab, var(--fg-on-red) 22%, transparent);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.ws-contact__info-name { color: var(--fg-on-red); }
.ws-contact__info-line { color: color-mix(in oklab, var(--fg-on-red) 82%, transparent); }
.ws-contact__info-links { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); margin-top: var(--space-2); }
.ws-contact__info .ws-link { --fg-muted: color-mix(in oklab, var(--fg-on-red) 78%, transparent); --fg: var(--fg-on-red); }

/* Phones: align the standalone dark components to the 32px page content inset.
   They own their padding via clamp() (portable), so the .mk-panel mobile rule
   never reaches them; here we bring only their HORIZONTAL padding down to 16px
   (16px .mk-container gutter + 16px = 32px). Vertical padding is left as-is so
   their tall, breathable proportions are preserved. --actions-small is covered
   too (those elements also carry .ws-contact; this later rule wins on left/right
   while its taller vertical padding stays). Desktop/tablet untouched. */
@media (max-width: 640px) {
  .ws-panel-glow,
  .ws-contact { padding-left: var(--space-4); padding-right: var(--space-4); }
}

/* inline form (part of Get in Touch) */
.form-inline {
  margin-top: 56px;
  background: color-mix(in oklab, var(--ws-tan-900) 30%, transparent);
  border-radius: var(--radius-md);
  padding: clamp(var(--space-8), 3.5vw, var(--space-12));
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: var(--shadow-lg);
}
/* form eyebrow = .section__eyebrow; component owns only spacing */
.form-inline__eyebrow { margin-bottom: var(--space-10); }
.form-inline__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6) var(--space-8);
}
.form-inline__row--full { grid-column: 1 / -1; }
/* fields + labels = DS .mk-field / .mk-input / .mk-label (form is .ws-dark scoped) */
.form-inline__actions {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-top: var(--space-6);
}
/* type from .ws-meta; component owns color */
.form-inline__hint {
  color: color-mix(in oklab, var(--fg-on-red) 60%, transparent);
}
.form-inline__success {
  text-align: center;
  padding: 40px 16px;
}
.form-inline__success-icon {
  width: 56px; height: 56px;
  margin: 0 auto 20px;
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--ws-tan-50) 25%, transparent);
  display: grid; place-items: center;
  color: var(--ws-tan-100);
  font-family: var(--font-display);
  font-size: var(--fs-xl);
}
/* type from .ws-h2; component owns spacing + color */
.form-inline__success-h {
  margin: 0 0 8px;
  color: var(--fg-on-red);
}
/* type from .ws-body; component owns spacing + color */
.form-inline__success-p {
  margin: 0;
  color: color-mix(in oklab, var(--fg-on-red) 80%, transparent);
}

/* Get-in-touch responsive */
@media (max-width: 980px) {
  .ws-contact__bar-text, .ws-contact__actions { grid-column: 1 / -1; }
  .form-inline__grid { grid-template-columns: 1fr; }
}


/* ============================================================
   ICON CARD  (.ws-card-icon)
   A .ws-card surface holding a Signature icon over a title + body.
   Use alongside .ws-card: <article class="ws-card-icon ws-card">.
   ============================================================ */
.ws-card-icon-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--grid-gutter); }
.ws-card-icon__viz {
  --guide: color-mix(in oklab, var(--ws-tan-400), var(--ws-tan-500));
  margin: 0 0 14px;
  min-height: 116px;
  display: grid; place-items: center;
}
.ws-card-icon__viz svg { width: 104px; height: 104px; }
.ws-card-icon .ws-h5 + .ws-body { margin-top: var(--space-3); }
@media (max-width: 980px) { .ws-card-icon-grid { grid-template-columns: 1fr; } }

/* ---- Highlight variant: the marked list becomes a full-bleed highlighted
   (purple-filled) panel spanning the card edge-to-edge at the bottom. It is
   pinned to the card's bottom (margin-top:auto) so the top content (icon,
   title, body) stays at the top and the flexible middle space absorbs any
   difference in text length — across a row of equal-height cards the panels
   bottom-align. Its content keeps the card's horizontal inset (26px) so items
   line up with the title/body above, and it reuses the card's 22px/26px
   padding rhythm. Reusable on any .ws-card-icon.ws-card ending in a
   .ws-list-marked. */
.ws-card-icon--highlight { overflow: hidden; }
/* description pins the band to the bottom (frees the band's top margin to be a
   real minimum gap rather than a collapsing auto). */
.ws-card-icon--highlight > .ws-body { margin-bottom: auto; }
.ws-card-icon--highlight .ws-list-marked {
  margin: var(--space-8) -26px -22px;
  padding: 22px 26px;
  min-height: 170px;
  align-content: start;
  background: var(--ws-purple);
  color: var(--fg-on-dark);
}
.ws-card-icon--highlight .ws-list-marked .ws-body { color: var(--fg-on-dark); }

/* ============================================================
   SIGNATURE ILLUSTRATION  (.ws-sig-illo)
   A larger conceptual "Engineered Line" illustration (see icon-style.md):
   dashed guide scaffold + light/deep purple concept-stroke pair + one red
   node. Inline SVG so colors stay on DS tokens. When the host .mk-reveal
   enters, the concept strokes draw in (pathLength="1"), then the red node
   settles and breathes. Reduced-motion shows the finished state.
   Markup: concept paths get .ws-sig-illo__stroke (+ --light / --deep) and
   pathLength="1"; the dot gets .ws-sig-illo__node; guides stay plain.
   ============================================================ */
.ws-sig-illo {
  --guide: color-mix(in oklab, var(--ws-tan-400), var(--ws-tan-500));
  margin-top: var(--space-8);
  width: clamp(240px, 30vw, 380px);
  --node-delay: 1200ms;
}
.ws-sig-illo svg { width: 100%; height: auto; display: block; overflow: visible; }

/* concept strokes — hidden, then drawn (staggered by --draw-i) when the host head reveals */
.ws-sig-illo__stroke {
  stroke-dasharray: 1; stroke-dashoffset: 1;
  transition: stroke-dashoffset 920ms var(--ease-out-soft);
  transition-delay: calc(var(--draw-i, 0) * 200ms);
}
.mk-reveal.in .ws-sig-illo__stroke { stroke-dashoffset: 0; }

/* red node — settles after the strokes, then breathes */
.ws-sig-illo__node {
  opacity: 0; transform: scale(0);
  transform-origin: center; transform-box: fill-box;
  transition: opacity 400ms var(--ease-out-soft) var(--node-delay, 1200ms), transform 400ms var(--ease-out-soft) var(--node-delay, 1200ms);
}
.mk-reveal.in .ws-sig-illo__node { opacity: 1; transform: scale(1); }
@media (prefers-reduced-motion: no-preference) {
  .mk-reveal.in .ws-sig-illo__node { animation: ws-sig-pulse 3.4s var(--ease-out-soft) calc(var(--node-delay, 1200ms) + 500ms) infinite; }
}
@keyframes ws-sig-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.16); } }

@media (prefers-reduced-motion: reduce) {
  .ws-sig-illo__stroke { stroke-dashoffset: 0 !important; transition: none !important; }
  .ws-sig-illo__node { opacity: 1 !important; transform: none !important; transition: none !important; animation: none !important; }
}

/* ============================================================
   ILLUSTRATION LINE SYSTEM  (ground rules — see shared/icon-style.md)
   Rendered stroke weight is CONSTANT at any display size: every
   illustration stroke uses vector-effect:non-scaling-stroke, so
   stroke-width is read in literal SCREEN PIXELS regardless of the
   illustration's viewBox or on-screen size. The inline stroke-width="…"
   numbers on the SVGs are therefore NOT authoritative — these rules are.
   Two tiers:
     • ICONS  ("Engineered Line" — .ws-sig-illo, .ws-card-icon__viz):
         concept strokes (solid) = 2px · dashed guide scaffold = 1px.
     • DIAGRAMS (process flow — .ws-sig-diagram):
         every line = 1px (connectors, dashed fan-in, grid, ticks, borders).
   The split inside icons is by the dashed-guide invariant: guides ALWAYS
   carry a stroke-dasharray attribute, concept strokes never do.
   Icons MAY animate (draw-in + node pulse); diagrams only reveal their
   parts — the lines themselves are not drawn-on.
   ============================================================ */
.ws-sig-illo svg *,
.ws-card-icon__viz svg *,
.ws-card-aurora__viz svg * { vector-effect: non-scaling-stroke; }
/* concept strokes (solid) — 2px */
.ws-sig-illo svg *:not([stroke-dasharray]),
.ws-card-icon__viz svg *:not([stroke-dasharray]),
.ws-card-aurora__viz svg *:not([stroke-dasharray]) { stroke-width: 2px; }
/* dashed guide scaffold — 1px */
.ws-sig-illo svg [stroke-dasharray],
.ws-card-icon__viz svg [stroke-dasharray],
.ws-card-aurora__viz svg [stroke-dasharray] { stroke-width: 1px; }

/* ============================================================
   MEDIA CARD  (.ws-card-media)
   A .ws-card pairing an image with text. Keeps the standard .ws-card
   surface + padding; the image is a contained, rounded element (an
   <image-slot> or <img> tagged .ws-card-media__media). Layout adapts
   to the card's own width (container query): wide/landscape → image
   left, content right; narrow/portrait → image on top, text below.
   Markup:
     <article class="ws-card ws-card-media">
       <div class="ws-card-media__layout">
         <image-slot class="ws-card-media__media" id="…" shape="rect" …></image-slot>
         <div class="ws-card-media__body"> …title + body + meta… </div>
       </div>
     </article>
   ============================================================ */
.ws-card-media { container-type: inline-size; }
.ws-card-media__layout { display: flex; flex-direction: column; gap: var(--space-5); }
.ws-card-media__media {
  flex: none;
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--ws-tan-100);
}
.ws-card-media__body { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.ws-card-media__body .ws-h5,
.ws-card-media__body .ws-body { margin: 0; }
.ws-card-media__body .ws-h5 + .ws-body { margin-top: var(--space-3); }
/* wide card (landscape) → image left, content right */
@container (min-width: 400px) {
  .ws-card-media__layout { flex-direction: row; align-items: stretch; }
  .ws-card-media__media { width: 42%; aspect-ratio: auto; align-self: stretch; min-height: 168px; }
  .ws-card-media__body { flex: 1; justify-content: center; }
}
/* Featured case card (.ws-case-feature): 50/50 split. Media column is fixed at
   half width and top-aligned; its height follows the browser frame's own ratio
   (bar + a 16/10 viewport). Project screenshots fill the frame width and crop
   the overflow (fit=cover) rather than letterboxing. */
.ws-case-feature .ws-browser__view { aspect-ratio: 16 / 10; }
/* Case-study grid cards get a taller screenshot frame than the default stacked
   card, so more of a dropped screenshot shows before the bottom crop. */
.ws-case-feature .ws-card-media--stacked .ws-card-media__media { aspect-ratio: 4 / 3; }
/* Card order: logo · title · description, then the screenshot last. */
.ws-case-feature .ws-card-media__body { order: 1; }
.ws-case-feature .ws-card-media__media { order: 2; }
/* ============================================================
   SHOTS CAROUSEL  (.ws-shots)  — drives shared/shots.js
   Up to 3 stacked <image-slot>s inside a browser frame; filled ones cross-fade
   (quick, 1s dwell) one at a time. Empty state shows the soft "blob" bg + the
   slot's own browse prompt. Hover reveals Replace / +Add / Remove and pauses.
   ============================================================ */
.ws-shots { position: relative; overflow: hidden; background: var(--shot-blob, none) center/cover no-repeat; }
.ws-shots--saas { --shot-blob: url('../images/case-logistics-a.webp'); }
.ws-shots--retail { --shot-blob: url('../images/case-retail-a.webp'); }
.ws-shots--automotive { --shot-blob: url('../images/case-automotive-a.webp'); }
.ws-shots--wholesale { --shot-blob: url('../images/case-wholesale-a.webp'); }
.ws-shots__slot { position: absolute; inset: 0; width: 100%; height: 100%; transition: opacity .28s ease-in-out; }
.ws-shots__slot::part(frame) { background: transparent; }
.ws-shots__slot::part(controls) { display: none; }
/* Empty screenshot slots show only the blob background — no middle prompt or
   dashed ring. Adding/replacing is via the hover toolbar (+ Add / Replace). */
.ws-shots__slot::part(empty), .ws-shots__slot::part(ring) { display: none; }
/* Empty-state ghost — a quiet, monochrome signature-style line icon suggestive
   of the case study, drawn faded over the blob until a real screenshot is
   added. Injected by shared/shots.js; auto-hidden once a slot is filled
   (.has-shot). Strokes only, single light-purple token, no red node. */
.ws-shots__ghost { position: absolute; top: 50%; left: 50%; width: 62%; max-width: 220px; aspect-ratio: 1; transform: translate(-50%, -50%); z-index: 1; pointer-events: none; color: var(--ws-purple-300); opacity: .42; transition: opacity .45s ease; }
.ws-shots__ghost .s { stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.ws-shots__ghost .g { display: none; }
.ws-shots.has-shot .ws-shots__ghost { opacity: 0; }
.ws-shots__dots { position: absolute; left: 0; right: 0; bottom: 8px; display: none; justify-content: center; gap: 6px; z-index: 3; }
.ws-shots__dot { width: 6px; height: 6px; border-radius: 50%; border: 0; padding: 0; cursor: pointer; background: rgba(255,255,255,.6); box-shadow: 0 0 0 1px rgba(0,0,0,.18); }
.ws-shots__dot.is-on { background: var(--ws-red); }
.ws-shots__bar { position: absolute; top: 8px; right: 8px; display: flex; gap: 6px; opacity: 0; pointer-events: none; transition: opacity .12s; z-index: 3; }
.ws-shots.is-editable.is-hover .ws-shots__bar { opacity: 1; pointer-events: auto; }
.ws-shots__btn { appearance: none; border: 0; border-radius: 6px; padding: 5px 10px; cursor: pointer; background: rgba(0,0,0,.65); color: #fff; font: 11px/1 system-ui, -apple-system, sans-serif; backdrop-filter: blur(6px); }
.ws-shots__btn:hover { background: rgba(0,0,0,.82); }
/* Tighter title↔description; the logo↔title gap comes from the logo band. */
.ws-case-feature .ws-card-media__body { gap: var(--space-1); }
.ws-case-feature .ws-card-media__body .ws-h5 + .ws-body { margin-top: 0; }
/* Case-study cards: 3-up on desktop; full width once the 12-col grid collapses
   to 6 (mk-grid ≤980px). !important overrides each card's inline grid-column. */
@media (max-width: 980px) {
  .ws-case-feature .mk-grid > article { grid-column: 1 / -1 !important; }
}
@container (min-width: 400px) {
  .ws-case-feature .ws-card-media__layout { align-items: flex-start; }
  .ws-case-feature .ws-card-media__media { width: 50%; align-self: flex-start; min-height: 0; aspect-ratio: auto; }
  .ws-case-feature .ws-card-media__body { flex: 1; justify-content: flex-start; }
}

/* ============================================================
   MARKED LIST  (.ws-list-marked)
   The Wildstream short-line list marker, generalised + portable: the
   .ws-contact__prompts idiom decoupled from the banner colors so it works on
   any surface. Marker + text take the container's text color (currentColor),
   so it reads correctly in both light and .ws-dark scopes.
   Markup: <ul class="ws-list-marked"><li class="ws-body">…</li>…</ul>
   ============================================================ */
.ws-list-marked {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--space-3);
  color: var(--fg-muted);
}
.ws-list-marked li { display: flex; align-items: center; gap: var(--space-4); }
.ws-list-marked li::before {
  content: ""; flex: none;
  width: 18px; height: 1px;
  background: color-mix(in oklab, currentColor 50%, transparent);
}
/* Ground rule: a marked list gets extra breathing room above it whenever it
   follows any sibling element (paragraph, heading, etc). Portable — applies
   everywhere. Override per-context if an exception is ever needed. */
* + .ws-list-marked { margin-top: var(--space-6); }
/* Two-column variant (desktop/tablet). Collapses to a single column on phones so
   the rows stack one after another instead of cramming two columns into a narrow
   screen. Row gap comes from the base .ws-list-marked. */
.ws-list-marked--2col { grid-template-columns: repeat(2, 1fr); column-gap: var(--space-12); }
@media (max-width: 640px) { .ws-list-marked--2col { grid-template-columns: 1fr; } }

/* ============================================================
   SECTION ATMOSPHERE  (.ws-atmos)
   A section/panel that clips a full-bleed animated background canvas
   (a "signature asset"). Put the canvas as .ws-atmos__bg and the
   content inside a .mk-container — content lifts above the canvas.
   The animation itself is a per-page <canvas> + script.
   ============================================================ */
.ws-atmos { position: relative; overflow: hidden; }
.ws-atmos__bg { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; pointer-events: none; }
.ws-atmos > .mk-container { position: relative; z-index: 1; }

/* ============================================================
   CLIP PANEL  (.ws-panel-clip)
   A panel that clips + isolates its children so an atmospheric/glow
   child (e.g. .ws-contact) is contained. Pair with .mk-panel--sunken
   for the tinted surface. __lede stacks section-head lead paragraphs.
   ============================================================ */
.ws-panel-clip { overflow: hidden; isolation: isolate; }
.ws-panel-clip__content { position: relative; z-index: 2; }
.ws-panel-clip__lede + .ws-panel-clip__lede { margin-top: var(--space-5); }
/* composition: a Get-in-Touch banner inside a clip panel gets top spacing */
.ws-panel-clip .ws-contact { margin-top: var(--space-16); }

/* ============================================================
   FOOTER  (.footer)
   Minimal page footer — full logo + eyebrow tagline + copyright,
   separated by a top hairline. Identical on every page.
   ============================================================ */
/* Footer paints no surface of its own — the page background always shows
   through, so it can never differ from the page (user rule). */
.footer { background: transparent; color: var(--fg); }
.footer__inner {
  border-top: 1px solid var(--border);
  padding-top: var(--space-8);
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--space-6) var(--space-8); flex-wrap: wrap;
}
.footer__brand-group {
  display: flex; flex-direction: row; align-items: center;
  gap: var(--space-6);
}
/* footer eyebrow: no line marker */
.footer .mk-eyebrow::before { display: none; }
.footer__logo { height: 38px; width: auto; display: block; }
.footer__copy { margin: 0; color: var(--fg-subtle); }

/* ============================================================
   STORY ILLUSTRATION  (.ws-story-illo)
   An "alive" canvas line-system composition (see shared/story-illos.js):
   a wider, animated sibling of .ws-sig-illo. Sits under a section-head
   title; the canvas tracks DS colour tokens and pauses off-screen
   (reduced-motion → static frame). Markup:
     <div class="ws-story-illo"><canvas data-story-illo="1"></canvas></div>
   ============================================================ */
.ws-story-illo { margin-top: var(--space-8); width: 100%; max-width: 480px; }
.ws-story-illo canvas { display: block; width: 100%; aspect-ratio: 1000 / 520; height: auto; }
/* ws-card-media variant: a story-illo canvas (shared/story-illos.js) as the
   card's media — the line-composition keeps its own proportion. */
.ws-card-media__media--illo { aspect-ratio: 1000 / 520; background: var(--ws-purple-25); }
.ws-card-media__media canvas { display: block; width: 100%; height: 100%; }
/* media as a centered client logo (real logo instead of imagery) —
   pair with the trimmed logo files in images/logos/case/trimmed/ so
   logos share a visual center and align across cards */
.ws-card-media__media--logo {
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-6);
  background: var(--bg);
}
.ws-card-media__media--logo img { display: block; max-width: 68%; max-height: 52px; width: auto; height: auto; }
/* ---------- Featured case study ----------
   ONE case study per industry detail page: eyebrow + a wide media card whose
   body leads with the client logo (trimmed files in images/logos/case/trimmed/).
   Owns its own top margin (sits below the two-discipline grid). Markup:
     <div class="ws-case-feature mk-reveal">
       <span class="mk-eyebrow">Case study</span>
       <article class="ws-card ws-card-media"> …media + body(logo, h3, p)… </article>
     </div> */
.ws-case-feature { margin-top: var(--space-12); display: flex; flex-direction: column; gap: var(--space-4); }
/* Logo sits in a fixed 80px band, vertically centered, flush-left; the mark is
   capped to 48px tall / 40% of the card wide, aspect preserved (never distorts). */
.ws-case-feature__logo-box { display: flex; align-items: center; justify-content: flex-start; height: 80px; margin-bottom: var(--space-5); }
.ws-case-feature__logo { display: block; width: auto; height: auto; max-width: 40%; max-height: 48px; }
/* Logo slots filled by drag-drop obey the same rules as author <img> logos:
   transparent, flush-left, capped to 48px tall / 40% of the band wide. */
image-slot.ws-case-feature__logo { width: 40%; height: 48px; }
image-slot.ws-case-feature__logo::part(frame) { background: transparent; }
/* ---------- Mini browser frame ----------
   Minimal simulated browser window for project imagery: hairline chrome,
   tan title bar, three window dots (one red node — signature-icon nod),
   pill address bar; the dropped image fills the viewport below. Pair with
   .ws-card-media__media (or any sized media container):
     <div class="ws-card-media__media ws-browser">
       <div class="ws-browser__bar" aria-hidden="true">
         <span class="ws-browser__dot ws-browser__dot--red"></span>
         <span class="ws-browser__dot"></span><span class="ws-browser__dot"></span>
         <span class="ws-browser__addr"></span>
       </div>
       <image-slot class="ws-browser__view" …></image-slot>
     </div> */
.ws-browser { display: flex; flex-direction: column; background: var(--bg); border: 1px solid var(--border-faint); }
.ws-browser__bar {
  flex: none; display: flex; align-items: center; gap: 6px;
  padding: 8px 12px;
  background: var(--ws-tan-100);
  border-bottom: 1px solid var(--border-faint);
}
.ws-browser__dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--ws-tan-300); }
.ws-browser__dot--red { background: var(--ws-red); }
.ws-browser__addr { flex: none; width: 38%; height: 8px; margin-left: 8px; border-radius: 999px; background: var(--bg); border: 1px solid var(--border-faint); }
.ws-browser__view { display: block; flex: 1; width: 100%; align-self: stretch; min-height: 0; }
/* stacked: force image-on-top at every width (overrides the wide-card row flip) */
.ws-card-media--stacked .ws-card-media__layout { flex-direction: column; align-items: stretch; }
.ws-card-media--stacked .ws-card-media__media { width: 100%; aspect-ratio: 1000 / 520; align-self: auto; min-height: 0; }
.ws-card-media--stacked .ws-card-media__body { justify-content: flex-start; }
