/* ============================================================
   Case study — shared structural classes
   ============================================================

   Reused across all 3 templates (early-stage / startups / enterprise).
   Every token here (--ff-*, --fs-*, --sp-*, --color-*, --accent,
   --radius-*, --measure, --container-max, --gutter) comes straight from
   the main site's style.css, loaded before this file on every case
   page — nothing here invents a new font, color, or spacing step
   (--ff-serif included: it's a real token in the main site's :root, the
   serif sibling of --ff-sans). Only the STRUCTURE (grids, rhythm,
   section shapes) is new, because that's what actually needs to differ
   between the three: how dense the copy is, whether features stack or
   alternate, whether it reads as one built thing or an inventory of
   many.

   Editorial, not product-UI: headings are the serif at a single normal
   weight (no bold), color does almost no work (one small accent dot
   near a heading or eyebrow, never a wash of blue across labels/
   numbers/dividers/borders), and structure comes from whitespace and
   type hierarchy rather than boxes, borders and dividing lines.
   ============================================================ */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--color-bg);
  color: var(--color-text-primary);
  font-family: var(--ff-sans);
  line-height: var(--lh-body);
}

.case-container {
  width: 100%;
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.case-back {
  position: fixed;
  top: var(--sp-5);
  left: var(--gutter);
  z-index: 10;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-13);
  color: var(--color-text-body);
  text-decoration: none;
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--radius-round);
  background: var(--card-bg);
  border: 1px solid var(--chip-border);
}
.case-back:hover { color: var(--color-text-primary); }

/* ---- hero ---- */
.case-hero { padding: calc(var(--sp-10) + var(--sp-5)) 0 var(--sp-9); }
.case-hero-grid {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: var(--sp-9);
  align-items: center;
}
/* Neutral, not blue -- an eyebrow label is a quiet wayfinding cue, not
   an accent moment. Color is reserved for one thing: the italic word
   inside a title (below). */
.case-kicker {
  margin: 0 0 var(--sp-3);
  font-family: var(--ff-mono);
  font-size: var(--fs-13);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
/* Serif, one weight (the face only ships one), fixed steps off the
   real type scale -- fs-44 desktop, fs-28 at the same golden-ratio-down
   relationship the hero captions use (see the @media block below), not
   a clamp() blending an invented rem value with viewport width. */
.case-title {
  margin: 0 0 var(--sp-5);
  font-family: var(--ff-serif);
  font-weight: var(--fw-regular);
  font-size: var(--fs-44);
  line-height: var(--lh-tight);
  letter-spacing: normal;
}
/* Italic, not colored -- in every reference this emphasis is style
   only, same ink as the rest of the headline. Color stays reserved for
   small things (a kicker, a tag, a dot), never the title itself. */
.case-title em { font-style: italic; }
.case-lede, .case-intro {
  margin: 0;
  max-width: var(--measure);
  font-size: var(--fs-18);
  line-height: var(--lh-prose);
  color: var(--color-text-body);
}

.case-facts {
  display: grid;
  grid-template-columns: repeat(2, auto);
  gap: var(--sp-5) var(--sp-8);
  margin: var(--sp-7) 0 0;
  padding: 0;
}
/* A short list of facts that should read as one row, not spread across
   the whole container: the 2-col `auto` grid above stretches its tracks
   to fill the width, which pushed the second fact far from the first. */
.case-facts--row { display: flex; flex-wrap: wrap; gap: var(--sp-5) var(--sp-8); }
.case-facts + .case-lede { margin-top: var(--sp-6); }
.case-facts div { display: flex; flex-direction: column; gap: var(--sp-1); }
.case-facts dt {
  font-family: var(--ff-mono);
  font-size: var(--fs-12);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
}
.case-facts dd { margin: 0; font-size: var(--fs-15); }

.case-meta-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-6);
}
.case-chip {
  font-size: var(--fs-13);
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--radius-round);
  border: 1px solid var(--chip-border);
  background: var(--card-bg);
  color: var(--color-text-body);
}

.case-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-6);
  padding: var(--sp-3) var(--sp-6);
  border-radius: var(--radius-round);
  background: var(--color-text-primary);
  color: var(--color-bg);
  font-size: var(--fs-14, var(--fs-13));
  text-decoration: none;
  font-weight: 500;
}

/* ---- media placeholder ---- */
/* No background fill, no drop shadow -- tried a soft shadow (the same
   token the site's own phone mockups use), but in both themes it
   rendered as its own visible box/halo around the image, exactly the
   "background" this was supposed to get away from.

   A hairline border is a different thing from that, and worth having:
   without it, a real screenshot with its own white/light background
   just cuts straight into the page's own background with no edge at
   all, which reads fine in light theme but looks like a stray white
   rectangle floating in dark theme. The same --chip-border token
   every chip/control already uses (defined per-theme, so light gets a
   light grey line and dark gets a dark grey one) draws just enough of
   an edge to separate the media from the page without becoming its
   own visible "card". Radius stays the standard --radius-card --
   a second reference showed a tighter, phone-screen-like corner, not
   the larger --radius-feature scale. */
.case-media {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--chip-border);
  border-radius: var(--radius-card);
  color: var(--color-text-muted);
  font-family: var(--ff-mono);
  font-size: var(--fs-12);
  text-align: center;
  overflow: hidden;
}
/* Skeleton loader -- a slow-loading image/video otherwise sat there as
   a flat, empty box with just a hairline border, which read as broken
   rather than "still loading". A subtle shimmer (the same --panel-bg-
   alt/--plate-chip tokens used elsewhere, so it's correctly muted in
   both themes with no new colors) fills the space until the media
   actually finishes loading, at which point script.js (or the case
   page's own small inline script, for a direct visit) adds
   `is-loaded` and this fades out. Pure CSS otherwise -- nothing here
   depends on JS to *look* right before that, only to know when to stop. */
.case-media::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    linear-gradient(100deg, transparent 35%, var(--plate-chip) 50%, transparent 65%),
    var(--panel-bg-alt);
  background-size: 200% 100%, 100% 100%;
  animation: caseMediaShimmer 1.6s ease-in-out infinite;
  opacity: 1;
  transition: opacity 0.4s var(--ease);
}
.case-media.is-loaded::before { opacity: 0; }
.case-media img, .case-media video { position: relative; z-index: 1; }
@keyframes caseMediaShimmer {
  0% { background-position: 200% 0, 0 0; }
  100% { background-position: -200% 0, 0 0; }
}
@media (prefers-reduced-motion: reduce) {
  .case-media::before { animation: none; }
}
/* Only the (now unused, every slot has a real photo) placeholder-text
   fallback needs breathing room -- an actual image fills edge to edge,
   no inset, so padding lives on the text alone instead of the
   container. */
.case-media span { max-width: 20ch; padding: var(--sp-4); }
.case-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Videos don't get the fixed-ratio cover treatment images do -- a real
   recording is rarely cut to exactly 9:17/16:10/1:1, and cropping it
   to fit one of those anyway was cutting off real content. Letting the
   container's own ratio go auto and sizing the video by width alone
   means the box takes on the video's OWN native ratio instead, showing
   the whole frame uncropped. */
.case-media:has(video) { aspect-ratio: auto; }
.case-media video { width: 100%; height: auto; display: block; }
/* Same 9:17 ratio, just smaller (80% of whatever width its own grid
   cell/column would otherwise give it) and centered within that cell
   -- these were reading as too dominant/large next to their copy,
   across every layout that uses this modifier (hero, feature blocks,
   the screens grid). */
.case-media--portrait { aspect-ratio: 9 / 17; max-width: 80%; margin-inline: auto; }
.case-media--landscape { aspect-ratio: 16 / 10; }
.case-media--square { aspect-ratio: 1 / 1; }
.case-media--wide { aspect-ratio: 21 / 9; }
/* Real bug: these feature-block recordings each bake their own device
   mockup/frame into the video itself, with a different amount of dead
   space around the actual screen -- letting each go fully native-ratio
   (the general video rule above) meant every one had a different
   visible letterbox and a different box height next to its copy.
   Forcing a shared 16:10 box + cover crops each recording in from its
   own edges, so the frame's own padding gets cropped away instead of
   shown, and every landscape feature block ends up the same height. */
.case-media--landscape:has(video) { aspect-ratio: 16 / 10; }
.case-media--landscape video { width: 100%; height: 100%; object-fit: cover; }

/* App badge -- the real product's own icon, small and in the corner
   of its feature recording. One extra element, no new layout: it
   just sits inside the media box .case-media already positions
   relative for (the skeleton-loader ::before uses the same anchor). */
/* .case-media img's own width:100%/height:100%/object-fit:cover rule
   (needed for the real screenshots) is more specific than a single
   class -- has to be beaten explicitly here, not just added to. */
.case-media img.case-app-badge {
  position: absolute;
  left: var(--sp-3);
  bottom: var(--sp-3);
  z-index: 2;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-round);
  background: #fff;
  box-shadow: var(--shadow-card);
  object-fit: contain;
}
/* Icons drawn as bare linework (no background baked in) need real
   padding so the mark doesn't touch the badge's own edge. */
.case-media img.case-app-badge--padded { padding: 7px; box-sizing: border-box; }

/* ---- sections ---- */
.case-section { padding: var(--sp-9) 0; }
/* No alternate background any more -- same "no boxes" rule as .case-media:
   a filled panel behind a whole section read as exactly the kind of
   background wash the rest of this page has been getting rid of. */
.case-section-head { max-width: 46rem; margin-bottom: var(--sp-8); }
.case-section-head .case-title { font-size: var(--fs-34); margin-bottom: var(--sp-4); }

.case-divider {
  width: 32px;
  height: 1px;
  background: var(--chip-border);
  margin: var(--sp-3) 0;
  border: none;
}

/* numbered principle/step grids */
.case-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-7);
}
/* The 4 steps read as one connected process now, not 4 separate boxes
   floating apart -- a single strip with a subtle grayscale gradient
   flowing across it (--card-bg -> --color-bg-subtle, both already
   theme-aware: a barely-there lightening in light mode, a barely-there
   lightening in dark mode too since --card-bg is the darker of the
   two there). The shift is deliberately subtle -- text contrast
   against either end stays effectively identical to a flat
   background, so this doesn't cost any accessibility in either theme.
   Hairlines between cells (not a gap) are what actually mark the step
   boundaries now. */
.case-grid-4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  background: linear-gradient(to right, var(--card-bg), var(--color-bg-subtle));
  border-radius: var(--radius-card);
  overflow: hidden;
}
.case-numbered {
  padding: var(--sp-5);
  border-left: 1px solid var(--chip-border);
}
.case-numbered:first-child {
  border-left: none;
}
.case-numbered span {
  display: block;
  font-family: var(--ff-mono);
  font-size: var(--fs-13);
  color: var(--color-text-muted);
  margin-bottom: var(--sp-2);
}
.case-numbered h3 { margin: 0 0 var(--sp-2); font-size: var(--fs-21); font-weight: 600; }
.case-numbered p { margin: 0; font-size: var(--fs-15); color: var(--color-text-body); }

/* alternating feature blocks (Startups pattern) */
/* No border-top between entries any more -- generous padding alone
   (var(--sp-8) on both sides of the seam) is what separates them;
   whitespace does the work a dividing line was doing before. */
.case-feature {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-8);
  align-items: center;
  padding: var(--sp-8) 0;
}
.case-feature-reverse .case-feature-copy { order: 2; }
.case-feature-reverse .case-feature-media { order: 1; }
.case-feature-index {
  font-family: var(--ff-mono);
  font-size: var(--fs-13);
  color: var(--color-text-muted);
  margin: 0 0 var(--sp-3);
}
.case-feature-copy h3 { margin: 0 0 var(--sp-3); font-size: var(--fs-28); font-weight: 600; }
.case-feature-copy p { margin: 0; font-size: var(--fs-15); color: var(--color-text-body); line-height: var(--lh-prose); }
.case-detail-list { list-style: none; margin: var(--sp-5) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.case-detail-list li {
  font-size: var(--fs-14, var(--fs-13));
  color: var(--color-text-body);
  padding-left: var(--sp-4);
  position: relative;
}
.case-detail-list li::before { content: "—"; position: absolute; left: 0; color: var(--accent); }

/* stacked feature blocks (Early Stage pattern) */
.case-feature-stack { display: flex; flex-direction: column; gap: var(--sp-9); }
/* Media | copy, side by side on desktop -- same 2-column idea as
   .case-feature, just its own class since these stack full-width on
   mobile instead of just re-flowing to 1fr. */
.case-feature-block {
  display: grid;
  grid-template-columns: 1fr 1.3fr;
  gap: var(--sp-7);
  align-items: center;
}
/* Same neutral bordered-pill treatment the main site's own project
   tags use (.card-tags span) -- one tag/pill language across the whole
   site, not a separate blue-washed one just for case pages. */
.case-tag-list { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-4); }
.case-tag-list span {
  font-size: var(--fs-12);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-round);
  border: 1px solid var(--chip-border);
  background: var(--card-bg);
  color: var(--ui-text);
}

/* A/B compare (Startups pattern) */
.case-compare {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: var(--sp-6);
  align-items: center;
  margin: var(--sp-8) 0;
}
.case-compare-arrow { font-size: var(--fs-28); color: var(--accent); }
.case-compare-card small {
  font-family: var(--ff-mono);
  font-size: var(--fs-12);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.case-compare-card h4 { margin: var(--sp-1) 0 var(--sp-2); font-size: var(--fs-18); }
.case-compare-card p { margin: 0 0 var(--sp-4); font-size: var(--fs-14, var(--fs-13)); color: var(--color-text-body); }

/* system-map / flow grid -- plain columns, no card box. Numbered steps
   read fine off type hierarchy and the grid's own gap alone; a bordered
   tile around each one was doing the same job the whitespace already
   does, twice. */
.case-flow-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-7);
  margin-top: var(--sp-7);
}

/* component board (Startups design-system pattern) */
.case-component-board {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-5);
  margin-top: var(--sp-7);
}
.case-component-tile {
  border-radius: var(--radius-card);
  overflow: hidden;
}
.case-component-label {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-12);
}
.case-component-label code {
  font-family: var(--ff-mono);
  font-size: var(--fs-11, 0.6875rem);
  color: var(--color-text-muted);
}
/* Growth-opportunities variant of the same tile: media on top, a
   plain title/description stack below it. */
.case-component-copy { padding: var(--sp-4) var(--sp-4) var(--sp-3); }
.case-component-copy h3 { margin: 0 0 var(--sp-2); font-size: var(--fs-18); }
.case-component-copy p:last-child { margin: 0; font-size: var(--fs-13); color: var(--color-text-body); }

/* 2-col split (brand / ownership sections) */
.case-split-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-8);
  align-items: center;
}

/* screens grid (Early Stage walkthrough pattern) */
.case-screens-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--sp-4);
  margin-top: var(--sp-7);
  align-items: start;
}
/* Real bug: a grid item's min-width defaults to the intrinsic size of
   its content, so a wide screenshot forced its 1fr track past its
   fair share -- squeezing the 5th column off-screen and adding a
   horizontal scrollbar on desktop. min-width:0 lets the track actually
   shrink to 1fr like the other four. */
.case-screens-grid > figure { min-width: 0; }
/* Real bug: these 5 screenshots share one real phone aspect ratio
   (414x896), but the box was a fixed HEIGHT with a free-floating width
   (the grid column) -- box shape didn't match image shape, so
   object-fit:contain letterboxed each phone with visible dead space on
   both sides. Matching the box's own aspect-ratio to the screenshots'
   real ratio makes the box the same shape as the image, so cover fills
   it edge to edge with no crop and no letterbox. */
.case-screens-grid .case-media { aspect-ratio: 414 / 896; height: auto; }
.case-screens-grid .case-media img { width: 100%; height: 100%; object-fit: cover; }
.case-screens-grid figcaption {
  margin-top: var(--sp-3);
  font-size: var(--fs-12);
  color: var(--color-text-body);
  display: flex;
  justify-content: center;
  gap: var(--sp-2);
}
.case-screens-grid figcaption strong { color: var(--accent); font-family: var(--ff-mono); }

/* enterprise: role/team meta panels -- whitespace separates entries,
   no dividing line (see .case-feature above for the same change). */
.case-role-panel {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: var(--sp-7);
  padding: var(--sp-6) 0;
}
.case-role-label {
  font-family: var(--ff-mono);
  font-size: var(--fs-12);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.case-role-name { font-size: var(--fs-18); font-weight: 600; margin-top: var(--sp-1); }
.case-role-tags { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-3); }

/* enterprise: repeated 50/50 initiative entries -- same whitespace-
   only separation as .case-feature/.case-role-panel above. */
.case-entry {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-8);
  align-items: center;
  padding: var(--sp-7) 0;
}
.case-entry-reverse .case-entry-media { order: 2; }
.case-entry-reverse .case-entry-copy { order: 1; }
.case-entry-eyebrow { font-size: var(--fs-15); font-weight: 600; margin: 0; }
.case-entry-sub { font-size: var(--fs-13); color: var(--color-text-muted); margin: var(--sp-1) 0 0; }
.case-entry-copy p.case-entry-desc { margin: var(--sp-3) 0 0; font-size: var(--fs-14, var(--fs-13)); color: var(--color-text-body); line-height: var(--lh-prose); }

.case-grid-2x2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-5) var(--sp-8);
  margin-top: var(--sp-2);
}

/* Closing island -- the real homepage .island component (unmodified:
   same yellow plate, same stats, same 3 contact rows, same drag/click
   JS), just embedded as page content instead of fixed to the
   viewport. All of .island/.island-bar/.island-panel/.island-row's own
   styling already comes from ../style.css, already linked on every
   case page -- the only thing that has to change is un-fixing it and
   bypassing the homepage-only "wait for the loading grid" opacity
   gate, since case pages have no loading grid to reveal it. */
.case-island-embed {
  display: flex;
  justify-content: center;
}
.case-island-embed .island {
  position: static;
  left: auto;
  bottom: auto;
  transform: none !important;
  opacity: 1 !important;
  translate: none !important;
}
/* Big hero-sized lead-in question above the island, same weight as
   the page's own h1 (.case-title's base size, not the smaller
   .case-section-head override). */
.case-island-title {
  margin-bottom: var(--sp-5);
  text-align: center;
}
/* Swaps in for .island-stats in this embedded context -- same plate,
   same ink, but a CTA line instead of the homepage's live stats. */
.island-cta-label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  width: 100%;
  font-size: var(--fs-15);
  font-weight: var(--fw-medium);
}
.island-cta-label .arrow {
  font-weight: var(--fw-regular);
  opacity: 0.7;
}

footer.case-footer {
  padding: var(--sp-9) 0 var(--sp-10);
  text-align: center;
  font-size: var(--fs-13);
  color: var(--color-text-muted);
}

/* Embedded in the project sheet (script.js's loadCaseContent fetches
   the standalone page and injects its <main>): no back link floating
   over the hero there (the sheet has its own close button), and no
   need for the standalone page's top clearance. */
.sheet-body .case-back { display: none; }
.sheet-body .case-hero { padding-top: 0; }
.sheet-body .case-container { padding: 0; max-width: none; }

.sheet-case { margin-top: var(--sp-7); }
.sheet-case:empty { margin-top: 0; }
.sheet-case[data-state="loading"] {
  margin-top: 0;
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Discreet, sharp -- mono (the same face the island's own stat
   readouts use), no motion beyond the number ticking, nothing trying
   to be a moment of its own. Simulated progress (see loadCaseContent
   in script.js), not measured off real bytes -- these are small local
   files today, but framed as a real loader for when they eventually
   aren't. */
.case-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--ff-mono);
}
.case-loading-mark {
  font-size: var(--fs-12);
  letter-spacing: 0.08em;
  color: var(--color-text-muted);
}
.case-loading-pct {
  font-size: var(--fs-13);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-body);
}

@media (max-width: 860px) {
  /* sp-9 top+bottom on adjacent sections stacks into ~190px of dead
     space between them on a phone -- keeps the separation (still a
     real gap, just not this large) without the two extra full steps
     desktop's two-column layouts actually need it for. */
  .case-section { padding: var(--sp-7) 0; }
  /* Bumped up a step for more impact on mobile -- fs-34 and fs-28
     still both existing tokens on the same scale, just one step closer
     to the desktop size (fs-44) than the previous fs-28/fs-21 pair. */
  .case-title { font-size: var(--fs-34); }
  .case-section-head .case-title { font-size: var(--fs-28); }
  .case-hero-grid, .case-feature, .case-feature-block, .case-split-2, .case-entry, .case-role-panel {
    grid-template-columns: 1fr;
  }
  /* sp-8's 64px gap was tuned for the side-by-side columns -- stacked
     into one column on mobile it reads as a huge gap between the
     heading and its own intro paragraph. */
  .case-split-2 { gap: var(--sp-4); }
  .case-feature-reverse .case-feature-copy { order: 1; }
  .case-feature-reverse .case-feature-media { order: 2; }
  /* Enterprise entries: the desktop zig-zag (text left / image right)
     is what reversal is for -- stacked on a phone it just made every
     other entry read text-then-image. Always image first, then text. */
  .case-entry-reverse .case-entry-media { order: 1; }
  .case-entry-reverse .case-entry-copy { order: 2; }
  /* One column, not two -- a 2-up grid on a narrow screen squeezed
     kicker/heading/body into a cramped half-width column instead of
     just reading top to bottom like everything else here does.
     Real bug: the gradient/divider on .case-grid-4 was built for a
     row of columns (left-to-right gradient, a left border between
     them) -- stacked into a single column, that same left-to-right
     gradient reads as an almost-flat sliver behind each full-width
     row instead of a visible flow, and the vertical divider hairline
     shows up as a stray short line with nothing for it to separate.
     Flip both to match the new stacking direction: top-to-bottom
     gradient, a top border between rows instead of a left one. */
  .case-grid-4 {
    grid-template-columns: 1fr;
    background: linear-gradient(to bottom, var(--card-bg), var(--color-bg-subtle));
  }
  .case-grid-4 .case-numbered {
    border-left: none;
    border-top: 1px solid var(--chip-border);
    padding: var(--sp-4);
  }
  .case-grid-4 .case-numbered:first-child {
    border-top: none;
  }
  .case-grid-4 .case-numbered span { margin-bottom: 2px; }
  .case-grid-4 .case-numbered h3 { margin-bottom: 2px; font-size: var(--fs-18); }
  .case-grid-4 .case-numbered p { font-size: var(--fs-13); }
  .case-flow-grid, .case-component-board { grid-template-columns: 1fr 1fr; }
  /* Same fix as the 4-step strip: a 2-up grid squeezed each numbered
     card into a narrow half-width column (words breaking one or two
     per line) and left a stray vertical hairline with nothing beside
     it. One column, tighter gap, a plain top divider between rows. */
  .case-grid-3 { grid-template-columns: 1fr; gap: 0; }
  .case-grid-3 .case-numbered {
    border-left: none;
    border-top: 1px solid var(--chip-border);
    padding: var(--sp-4) 0;
  }
  .case-grid-3 .case-numbered:first-child { border-top: none; padding-top: 0; }
  .case-grid-3 .case-numbered span { margin-bottom: 2px; }
  .case-grid-3 .case-numbered h3 { margin-bottom: 2px; font-size: var(--fs-18); }
  .case-grid-3 .case-numbered p { font-size: var(--fs-13); }
  .case-screens-grid { grid-template-columns: repeat(2, 1fr); }
  .case-screens-grid figcaption { justify-content: center; }
  .case-compare { grid-template-columns: 1fr; }
  .case-compare-arrow { transform: rotate(90deg); justify-self: center; }
  .case-grid-2x2 { grid-template-columns: 1fr; }
}

/* The embedded CTA is a click target that opens the contact modal
   (script.js), not a draggable island: no grabber, no inline panel,
   and the trailing dot cursor stays in charge. */
.case-island-embed .island-grabber,
.case-island-embed .island-panel { display: none; }

.case-loading-label {
  font-size: var(--fs-12);
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}
