/* ─────────────────────────────────────────────────────────
   Stylesheet for the case-study template (header / overview /
   problem / research / process / solution / results /
   reflection rail structure). Tokens, base resets, type roles,
   and the nav bar are shared with the homepage via shared.css
   (loaded before this file) — only the case-study-specific
   layout tokens and components live here.
   ───────────────────────────────────────────────────────── */

:root {
  --rail-w: 8.3333%;
  /* vw, not %: this is referenced from elements whose containing block isn't
     always the full frame (e.g. .bonus-block's edge-to-edge bleed, nested
     inside the narrower content column) — % would resolve against whichever
     box happens to reference it, throwing off the bleed math. vw is always
     relative to the viewport, so every usage agrees on the same pixel value. */
  --gutter: clamp(20px, 8.3333vw, 120px);
}

/* ── Frame / nav (site-nav/nav-brand/skip-link are in shared.css) ──
   .frame is full-bleed so .site-nav spans the entire viewport width;
   #main carries the 1400px content cap instead, matching the homepage's
   pattern of a full-width frame with individually capped inner sections. */
/* overflow-x:hidden clips full-bleed elements like .bonus-block, whose
   negative margins are sized off --gutter (a vw value). vw measures the
   whole viewport including the scrollbar's reserved strip, but the page's
   actual content area excludes it, so the bleed overshoots the true edge
   by the scrollbar's width and would otherwise make the page horizontally
   scrollable by that same amount. */
.frame { position: relative; width: 100%; background: var(--bg); overflow-x: hidden; }
#main { max-width: 1400px; margin: 0 auto; }

/* ── "View more case studies" nav menu ── */
.nav-more { position: relative; }
.nav-more-btn {
  display: flex; align-items: center; gap: 12px; min-height: 44px; padding: 0;
  background: none; border: none; font: inherit; letter-spacing: inherit; text-transform: inherit;
  color: inherit; cursor: pointer; transition: color .2s;
}
.nav-more-btn:hover, .nav-more-btn.is-open { color: var(--accent); }
.nav-more-icon { display: grid; grid-template-columns: repeat(3,3px); gap: 3px; flex: none; }
.nav-more-icon span { width: 3px; height: 3px; background: currentColor; }

.more-panel {
  position: absolute; top: calc(100% - 1px); right: 0; z-index: 60;
  width: 400px; max-width: calc(100vw - 40px); max-height: min(640px, calc(100vh - 120px)); overflow: hidden; box-sizing: border-box;
  background: var(--bg); color: var(--ink); border: 1px solid var(--ink-18);
  box-shadow: 0 18px 40px -18px rgba(22,22,26,.3);
  opacity: 0; pointer-events: none; transform: translateY(-8px);
  transition: opacity .3s ease, transform .35s cubic-bezier(.22,.61,.36,1);
}
@media (max-width: 767px) { .more-panel { right: -20px; } }

.more-item { opacity: 0; transform: translateY(10px); transition: opacity .4s ease, transform .4s cubic-bezier(.22,.61,.36,1), background .25s ease; }

.more-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px;
  box-shadow: inset 0 -1px 0 var(--ink-18);
  font: 500 10px/1 var(--font-mono); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-60);
}
/* Second group heading (Side quests): extra top padding sets it apart
   from the case-study list above it. */
.more-subhead { padding-top: 28px; }
.more-close { display: flex; align-items: center; justify-content: center; width: 44px; height: 28px; margin: -8px -12px -8px 0; padding: 0; background: none; border: none; color: inherit; cursor: pointer; }
.more-close:hover { color: var(--accent); }
.more-close-icon { position: relative; width: 14px; height: 14px; flex: none; }
.more-close-icon span { position: absolute; left: 0; top: 50%; width: 14px; height: 1px; background: currentColor; }
.more-close-icon span:first-child { transform: rotate(45deg); }
.more-close-icon span:last-child { transform: rotate(-45deg); }

.more-link, .more-current {
  display: flex; align-items: center; gap: 14px; min-height: 60px; padding: 11px 20px; box-sizing: border-box;
  box-shadow: inset 0 -1px 0 rgba(22,22,26,.14);
}
.more-link { text-decoration: none; color: var(--ink); }
.more-link:hover { background: rgba(43,43,240,.06); }
.more-current { background: rgba(43,43,240,.05); }
.more-link-body { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.more-link-title { font: 500 15px/1.3 var(--font-display); letter-spacing: -.02em; }
.more-current .more-link-title { color: var(--accent); }
.more-link-meta { font: 500 10px/1 var(--font-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-68); }
.more-lock-tag { color: var(--accent); }
/* A fixed-size slot (matching the bigger lock diamond below) rather than
   sizing the element to the small dot itself - so a plain diamond's center
   lines up with a locked row's bigger one instead of both merely sharing a
   right edge, which reads as the plain dots drifting off to the side. */
.more-link-dot { width: 18px; height: 18px; flex: none; display: flex; align-items: center; justify-content: center; }
.more-link-dot::before { content: ''; width: 7px; height: 7px; flex: none; transform: rotate(45deg); border: 1px solid rgba(22,22,26,.4); }
.more-current .more-link-dot::before { background: var(--accent); border: none; }
/* Locked rows swap the plain diamond for a bigger one that frames a padlock,
   mirroring the homepage's work-row treatment (.row-lock-icon) - sized up
   just enough for the icon to read at this scale. Unlocking a case study
   this session doesn't remove the icon, it opens the shackle in place (see
   refreshLockTags in case-study.js), same as the homepage. */
.more-link-dot.more-lock-dot { border: 1px solid var(--accent); background: var(--bg); color: var(--accent); transform: rotate(45deg); }
.more-link-dot.more-lock-dot::before { content: none; }
.more-current .more-link-dot.more-lock-dot { background: var(--bg); border: 1px solid var(--accent); }
.more-lock-dot svg { flex: none; transform: rotate(-45deg); }

.more-foot { padding: 12px 20px; font: 500 10px/1 var(--font-mono); letter-spacing: .16em; text-transform: uppercase; }
.more-foot a { text-decoration: none; color: var(--ink-68); display: flex; justify-content: space-between; align-items: center; min-height: 28px; transition: color .2s; }
.more-foot a:hover { color: var(--accent); }

/* ── Rail grid: every major block (header, meta strip, sections, pager)
   is a 4-column grid — rail | spacer | content | margin — so the rail's
   vertical label and node sit in a cell that is exactly as tall as the
   content beside it, growing and scrolling with that content instead of
   sitting in one fixed, centered stack. The spine (the right border on
   each rail cell) lines up column-to-column so it reads as one continuous
   line down the page. ── */
.case-header,
.case-meta-row,
.case-section,
.case-pager {
  position: relative;
  display: grid;
  grid-template-columns: var(--gutter) var(--gutter) minmax(0,1fr) var(--gutter);
}
.rail-cell { grid-column: 1; position: relative; box-shadow: inset -1px 0 0 var(--ink-18); }
.rail-node {
  position: absolute; left: 100%; top: 0; width: 9px; height: 9px; z-index: 61;
  transform: translate(-50%,-50%) rotate(45deg);
  background: var(--bg); border: 1px solid var(--accent);
  transition: background .45s ease, border-color .45s ease;
}
.rail-node.is-on { background: var(--accent); }
.row-content { grid-column: 3; position: relative; min-width: 0; }
@media (max-width: 899px) {
  .case-header, .case-meta-row, .case-section, .case-pager { grid-template-columns: 20px minmax(0,1fr) 20px; }
  .rail-cell { display: none; }
  .row-content { grid-column: 2; }
  /* .site-nav otherwise keeps using the fluid --gutter (up to 120px) even
     at this breakpoint, while the content columns above just switched to
     a flat 20px - matching it here keeps the nav's brand/links aligned
     with the case-study content once the rail disappears. */
  .site-nav { padding: 0 20px; }
}

/* ── Header (00) ── */
/* Top and bottom spacing live on the content column, not the grid
   container, so the grid row (and the rail-cell that stretches to match
   it) spans the full header height - the rail line then runs from the
   nav divider straight through to the next section with no gap. */
.case-header .row-content { padding: 64px 0 80px; }
.case-header-grid {
  position: absolute; inset: 0; pointer-events: none;
  background-image: linear-gradient(to right, var(--ink-09) 0 1px, transparent 1px), linear-gradient(to bottom, rgba(22,22,26,.075) 0 1px, transparent 1px);
  background-size: var(--rail-w) 96px;
  mask-image: radial-gradient(ellipse 48% 55% at 50% 50%, #000 0%, rgba(0,0,0,.35) 55%, transparent 88%);
  -webkit-mask-image: radial-gradient(ellipse 48% 55% at 50% 50%, #000 0%, rgba(0,0,0,.35) 55%, transparent 88%);
}
.case-header .t-title { margin-top: 32px; max-width: 900px; }
.case-header .t-bodylead { margin-top: 28px; max-width: 640px; color: var(--ink-62); }
.case-badge {
  display: inline-flex; align-items: center; gap: 8px; height: 28px; padding: 0 12px;
  margin-top: 20px; background: var(--ink); border: 1px solid var(--ink); color: var(--dark-muted-85);
  font: 500 11px/1 var(--font-mono); letter-spacing: .14em; text-transform: uppercase;
  transition: background .2s;
}
/* When .case-badge is a link (e.g. jumping to the Hypesite bonus block),
   override the base a:hover color rule - otherwise hover text turns
   var(--ink), matching the badge's own background and disappearing. */
a.case-badge:hover { background: #000; color: var(--dark-muted-85); }
.case-badge .dot { width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--prism); background-size: 200% 100%; animation: prism 6s linear infinite; }

/* Hero screenshot - a second row-content inside .case-header, below the
   intro copy, still part of the header rail section (its rail-cell
   carries no node of its own - the section's single node already
   marks the top of this row group). */
.case-header .row-content.case-hero { padding: 0 0 96px; }
/* Lock gate relocated above the hero (see the gated case studies): its
   locked/unlocked children still render as a .case-section, which lays
   out its own rail-cell/row-content pair independently - this rule only
   needs to stop the wrapper itself from collapsing into the rail's
   first-column track now that it's a direct grid item of .case-header. */
.case-header > [data-lock-gate] { grid-column: 1 / -1; }
.case-hero .placeholder { aspect-ratio: 16/9; }
.case-hero .hero-image, .case-hero .hero-video { display: block; width: 100%; aspect-ratio: 16/9; height: auto; object-fit: cover; object-position: top; }
/* Pause/play for a looping hero video (WCAG 2.2.2: anything that moves
   for more than 5s needs a way to stop it). Sits inside .hero-frame's
   14px crop-mark padding, in the video's bottom-right corner. */
.video-toggle {
  position: absolute; right: 24px; bottom: 24px; width: 36px; height: 36px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(22,22,26,.72); color: #fff; border: none; cursor: pointer; transition: background .2s;
}
.video-toggle:hover { background: var(--ink); }
.video-toggle:focus-visible { outline: 2px solid var(--accent-focus); outline-offset: 2px; }
.video-toggle .icon-play, .video-toggle.is-paused .icon-pause { display: none; }
.video-toggle.is-paused .icon-play { display: block; }
.case-hero .placeholder-caption { max-width: 747px; margin-top: 24px; }

/* Crop-mark frame - four corner brackets set outside the hero image by
   the frame's own padding, like a photo ready to be cropped to bleed. */
.hero-frame { position: relative; padding: 14px; }
.hero-frame .crop-mark { position: absolute; width: 22px; height: 22px; border: 0 solid var(--accent); }
.hero-frame .crop-mark.tl { left: 0; top: 0; border-left-width: 1px; border-top-width: 1px; }
.hero-frame .crop-mark.tr { right: 0; top: 0; border-right-width: 1px; border-top-width: 1px; }
.hero-frame .crop-mark.bl { left: 0; bottom: 0; border-left-width: 1px; border-bottom-width: 1px; }
.hero-frame .crop-mark.br { right: 0; bottom: 0; border-right-width: 1px; border-bottom-width: 1px; }

/* ── Meta strip (Client / Timeline / Status / Outcome) ── */
.case-meta-row .row-content { box-shadow: inset 0 1px 0 var(--ink-18), inset 0 -1px 0 var(--ink-18); }
.case-meta { margin: 0; display: grid; grid-template-columns: repeat(4,1fr); padding: 24px 0; }
.case-meta > div { padding: 0 24px 0 0; }
.case-meta > div:not(:last-child) { box-shadow: inset -1px 0 0 var(--ink-18); }
.case-meta > div:not(:first-child) { padding-left: 20px; }
.case-meta dt { color: var(--ink-68); }
.case-meta dd { margin: 8px 0 0; }
@media (max-width: 767px) {
  .case-meta { grid-template-columns: 1fr 1fr; row-gap: 20px; }
  .case-meta > div:nth-child(2) { box-shadow: none; }
  .case-meta > div:nth-child(3) { padding-left: 0; box-shadow: inset -1px 0 0 var(--ink-18); }
}

/* ── Sections (01 Overview onward) ── */
.case-section .row-content { padding: 80px 0; box-shadow: inset 0 1px 0 var(--ink-14); }
/* A section that is only a dark bonus block (e.g. Next-Gen's "UHF Bot")
   has no heading of its own, so its top divider and top padding would
   just float above the block. The block's own margin spaces it instead. */
.case-section.is-bonus .row-content { padding-top: 0; box-shadow: none; }
.case-section#overview .row-content { box-shadow: none; }
.case-section .t-label { color: var(--accent); }
.case-section .t-sectionhead { margin-top: 20px; max-width: 46ch; }
.case-section .t-lede { margin-top: 32px; max-width: 800px; }
.case-section .t-bodylead { margin-top: 20px; max-width: 68ch; color: var(--ink-68); }
.case-section .t-body { margin-top: 16px; max-width: 68ch; color: var(--ink-60); }
.case-section .t-body + .t-body { margin-top: 12px; }

.callout {
  margin-top: 64px; padding: 32px 0 32px 32px; max-width: 800px; box-sizing: border-box;
  border-left: 2px solid var(--accent); color: var(--ink);
}
.callout .t-label { color: var(--accent); }
.case-section .callout p { margin-top: 16px; font: 400 19px/1.6 var(--font-body); text-wrap: pretty; color: var(--ink); }

.bullet-list { margin-top: 20px; display: flex; flex-direction: column; gap: 12px; max-width: 68ch; }
.bullet-list li { display: flex; gap: 12px; color: var(--ink-60); font: 400 16px/1.6 var(--font-body); }
.bullet-list li::before { content: ''; width: 6px; height: 6px; margin-top: 9px; flex: none; background: var(--accent); transform: rotate(45deg); }

/* Numbered step list ("Design process" sections): a two-column grid, index
   number beside heading+body, with a divider rule above each row. .step
   uses display:contents so its .num/.body children become direct grid
   items - that's what lets one shared box-shadow-per-cell draw a single
   continuous divider line across both columns, row by row. */
.step-list { margin-top: 64px; display: grid; grid-template-columns: minmax(0,1fr) minmax(0,8fr); }
.step-list .step { display: contents; }
.step-list .step .num {
  padding-top: 20px; box-shadow: inset 0 1px 0 rgba(22,22,26,.15);
  font: 400 13px/1.7 var(--font-mono); letter-spacing: .16em; color: var(--accent);
}
.step-list .step .body { padding: 20px 0 32px; box-shadow: inset 0 1px 0 rgba(22,22,26,.15); }
.step-list .step .body h3 { margin: 0; }
.step-list .step .body p { margin: 12px 0 0; max-width: 747px; color: var(--ink-60); }
.step-list .step .body p + p { margin-top: 20px; }
@media (max-width: 767px) {
  .step-list { grid-template-columns: 1fr; gap: 20px; }
  .step-list .step .num, .step-list .step .body { box-shadow: none; padding: 0; }
}

/* Label/value divider grid - Overview's Scope/Team/Tools rows */
.info-grid { margin-top: 64px; display: grid; grid-template-columns: minmax(0,2fr) minmax(0,7fr); }
.info-grid > .info-label { padding: 24px 32px 24px 0; box-shadow: inset 0 1px 0 var(--ink-18); color: var(--ink-68); }
/* margin-top: 0 overrides .case-section .t-body's margin-top: 16px, which
   otherwise also matches this cell (it carries the t-body type-role class)
   and pushes it 16px below the row's top divider, out of line with
   .info-label. */
.info-grid > .info-value { margin-top: 0; padding: 24px 0; box-shadow: inset 0 1px 0 var(--ink-18); color: rgba(22,22,26,.7); }
@media (max-width: 767px) { .info-grid { grid-template-columns: 1fr; } .info-grid > .info-label { padding-bottom: 8px; box-shadow: none; } .info-grid > .info-value { padding-top: 8px; } }

/* Two-column cost/risk grid - Problem section */
.cost-grid { margin-top: 40px; display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 40px 64px; }
.cost-item { padding-top: 20px; box-shadow: inset 0 1px 0 rgba(22,22,26,.25); }
.cost-item .t-label { color: var(--accent); }
.cost-item p { margin-top: 16px; color: rgba(22,22,26,.7); }
@media (max-width: 767px) { .cost-grid { grid-template-columns: 1fr; } }

/* Two/three-column heading+body grid - Research section */
.research-grid { margin-top: 40px; display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 40px 64px; }
.research-grid.cols-3 { grid-template-columns: repeat(3,minmax(0,1fr)); gap: 40px; }
.research-grid h3 { margin: 0; }
.research-grid p { margin-top: 14px; color: rgba(22,22,26,.65); }
@media (max-width: 767px) { .research-grid, .research-grid.cols-3 { grid-template-columns: 1fr; } }

/* Three-column card grid with dividers - Solution section */
.solution-grid { margin-top: 64px; display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); }
.solution-grid > div { padding: 20px 32px 0 0; box-shadow: inset 0 1px 0 var(--ink-18); }
.solution-grid > div:not(:first-child) { padding-left: 20px; box-shadow: inset 0 1px 0 var(--ink-18), inset 1px 0 0 var(--ink-18); }
.solution-grid > div:last-child { padding-right: 0; }
.solution-grid h3 { margin: 0; }
.solution-grid p { margin-top: 14px; color: var(--ink-68); }
@media (max-width: 767px) {
  .solution-grid { grid-template-columns: 1fr; gap: 32px; }
  .solution-grid > div, .solution-grid > div:not(:first-child) { padding: 20px 0 0; box-shadow: inset 0 1px 0 var(--ink-18); }
}

/* Screens sub-header - divider bar above a hero image + grid */
.screens-header {
  margin-top: 96px; height: 96px; display: flex; align-items: center; justify-content: space-between;
  box-shadow: inset 0 1px 0 var(--ink-18); color: var(--ink-68);
}
.screens-header span:first-child { color: var(--accent); }
.screens-header + .screens-grid { margin-top: 48px; }

/* Screens / image-slot placeholders */
.screens-grid { margin-top: 32px; display: grid; grid-template-columns: repeat(2,1fr); gap: 20px; }
.screens-grid.single { grid-template-columns: 1fr; }
.placeholder {
  position: relative; width: 100%; display: flex; align-items: center; justify-content: center; text-align: center;
  background: repeating-linear-gradient(135deg, rgba(120,120,140,.10) 0 10px, rgba(120,120,140,.04) 10px 20px);
  border: 1px dashed rgba(120,120,140,.45);
  color: rgb(90,90,110);
  font: 500 11px/1.4 var(--font-mono); letter-spacing: .1em; text-transform: uppercase;
  padding: 16px;
}
.placeholder-caption { margin-top: 10px; color: var(--ink-68); font: 400 13px/1.6 var(--font-mono); }
.screens-grid .screen-image { width: 100%; height: auto; display: block; aspect-ratio: 4/3; object-fit: cover; background: #fff; border: 1px solid rgba(120,120,140,.25); }
@media (max-width: 767px) { .screens-grid { grid-template-columns: 1fr; } }

/* Side quests: image-led pages reusing the case-study shell. Each slot is
   a .placeholder until a real image replaces it (as .screen-image). */
.screen-placeholder { aspect-ratio: 4/3; }
.screen-placeholder.is-wide, .case-hero .screen-placeholder { aspect-ratio: 16/9; }
.screens-grid .screen-image.is-wide { aspect-ratio: 16/9; }
/* .is-natural: each image keeps its own proportions and height (e.g. tall
   phone screens of different lengths) instead of the 4:3 crop, and rows
   align to the top rather than stretching to the tallest item. No frame
   (background/border): these are cut-out shots that bring their own edges. */
.screens-grid.is-natural { align-items: start; }
.screens-grid.is-natural .screen-image { aspect-ratio: auto; object-fit: initial; background: none; border: none; }
/* Opt back into the thin frame for natural-ratio shots of white UI
   (spreadsheets, web pages) whose edges would melt into the page. */
.screens-grid.is-natural .screen-image.is-framed { background: #fff; border: 1px solid rgba(120,120,140,.25); }
.screens-grid figure { margin: 0; }
.screens-grid + .screens-grid { margin-top: 20px; }
/* Side-quest galleries are image-led, so rows get more breathing room -
   both between rows inside a grid and between consecutive grids. */
#gallery .screens-grid { row-gap: 48px; }
#gallery .screens-grid + .screens-grid { margin-top: 48px; }

/* "More screens coming soon" notice - replaces a full screens-grid where
   the additional screenshots aren't cleared for publication yet. */
.screens-coming-soon {
  margin-top: 48px; padding: 48px 40px; max-width: 960px; box-sizing: border-box;
  border: 1px dashed rgba(120,120,140,.45); background: rgba(120,120,140,.04);
}
.screens-coming-soon .t-label { color: var(--accent); }
.screens-coming-soon p { margin: 20px 0 0; max-width: 640px; color: var(--ink-60); }

/* Content-pending notice (used where real narrative text could not be
   sourced — see report). Visually distinct from real copy so it can't be
   mistaken for finished content. */
.pending {
  margin-top: 20px; max-width: 68ch; padding: 18px 20px;
  border: 1px dashed rgba(120,120,140,.5); background: rgba(120,120,140,.06);
  color: rgba(90,90,110,.95); font: 400 13px/1.7 var(--font-mono);
}

/* Dark "bonus block" (e.g. the Hypesite / UHF Bot asides) */
.bonus-block {
  margin-top: 32px; padding: 40px var(--gutter); margin-left: calc(-1 * var(--gutter)); margin-right: calc(-1 * var(--gutter));
  background: var(--dark-bg); color: var(--dark-ink); position: relative; overflow: hidden;
}
/* A bonus block following a callout (e.g. Broadacre's Key decision ->
   Hypesite) needs more air than the default - the callout's own accent
   rule and this block's dark background otherwise read as running
   together with only the callout's bottom padding between them. */
.callout + .bonus-block { margin-top: 64px; }
.bonus-block::before {
  content: ''; position: absolute; left: 0; top: 0; width: 100%; height: 3px; background: var(--prism);
  background-size: 200% 100%; animation: prism 6s linear infinite;
}
.bonus-block .t-label { color: var(--accent-dark); }
.bonus-block .t-sectionhead { margin-top: 14px; color: var(--dark-ink); }
.bonus-block .t-body { margin-top: 14px; color: var(--dark-muted-85); }

.hypesite-chips { margin-top: 24px; display: flex; flex-wrap: wrap; gap: 10px; }
.hypesite-chips span {
  display: inline-flex; align-items: center; height: 28px; padding: 0 14px;
  border: 1px solid var(--dark-muted-30); color: var(--dark-muted-85);
  font: 500 11px/1 var(--font-mono); letter-spacing: .12em; text-transform: uppercase;
}
.hypesite-cta { margin-top: 24px; display: flex; align-items: center; flex-wrap: wrap; gap: 14px; }
/* .hypesite-link.t-label (rather than plain .hypesite-link) so this beats
   .bonus-block .t-label's color rule above - same 2-class specificity, and
   this one comes later in the file. */
.hypesite-link.t-label {
  display: inline-flex; align-items: center; gap: 8px; height: 44px; padding: 0 20px;
  background: var(--accent); color: #fff; text-decoration: none;
  transition: background .2s, transform .2s, box-shadow .2s;
}
.hypesite-link.t-label:hover { background: #2222c0; transform: translateY(-1px); box-shadow: 0 10px 24px -10px rgba(0,0,0,.6); }

/* Bonus block layout: text column + a fake "app window" mockup column.
   Below 1023px the mock is wide enough (fixed 640px, see .mock-window)
   that squeezing it into a side column just breaks the diagram - so the
   grid stacks instead, and the window itself shrinks via zoom rather
   than reflowing its internal columns. */
.bonus-grid { margin-top: 24px; display: grid; grid-template-columns: minmax(0,4fr) minmax(0,6fr); gap: 64px; align-items: start; }
@media (max-width: 1023px) { .bonus-grid { grid-template-columns: minmax(0,1fr); gap: 40px; } .bonus-grid > *:last-child { overflow-x: auto; } }
@media (max-width: 767px) { .bonus-grid { grid-template-columns: minmax(0,1fr); gap: 32px; } }
@keyframes pulseDot { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

/* Fixed-width "screenshot" that scales as a whole via zoom instead of
   reflowing internally, so the sidebar/content diagram stays intact
   (just smaller) all the way down to narrow phones. */
.mock-window { border: 1px solid rgba(244,243,239,.22); background: rgba(244,243,239,.04); font: 400 13px/1.7 var(--font-mono); }
@media (max-width: 1226px) { .mock-window { width: 640px; zoom: .84; } }
@media (max-width: 1152px) { .mock-window { zoom: .79; } }
@media (max-width: 1088px) { .mock-window { zoom: .74; } }
@media (max-width: 1023px) { .mock-window { width: auto; zoom: 1; } }
@media (max-width: 679px) { .mock-window { width: 640px; zoom: .93; } }
@media (max-width: 639px) { .mock-window { zoom: .81; } }
@media (max-width: 559px) { .mock-window { zoom: .68; } }
@media (max-width: 479px) { .mock-window { zoom: .56; } }
@media (max-width: 399px) { .mock-window { zoom: .43; } }
.mock-titlebar {
  height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 14px;
  border-bottom: 1px solid rgba(244,243,239,.18); color: var(--dark-muted-58); gap: 10px; flex-wrap: wrap;
}
.mock-brand { display: flex; align-items: center; gap: 8px; }
.mock-brand strong { color: var(--dark-ink); font-weight: 500; }
.mock-chip { padding: 3px 6px; border: 1px solid rgba(244,243,239,.25); font-size: 9px; }
.mock-tabs, .mock-actions { display: flex; align-items: center; gap: 6px; }
.mock-actions { gap: 10px; color: var(--dark-muted-58); }
.mock-tab { padding: 5px 10px; }
.mock-tab.is-active { background: rgba(111,111,255,.35); color: var(--dark-ink); }

.mock-body { display: grid; grid-template-columns: minmax(0,190px) minmax(0,1fr); }
.mock-sidebar { border-right: 1px solid rgba(244,243,239,.18); padding: 16px 12px; display: flex; flex-direction: column; gap: 16px; color: var(--dark-muted-58); }
.mock-sidebar-group { display: flex; flex-direction: column; gap: 8px; }
.mock-group-label { color: rgba(244,243,239,.35); letter-spacing: .14em; text-transform: uppercase; }
.mock-sidebar-item { padding: 8px 10px; border: 1px solid rgba(244,243,239,.18); }
.mock-sidebar-item.is-active { background: rgba(111,111,255,.3); color: var(--dark-ink); border-color: transparent; }
.mock-sidebar-row { display: flex; justify-content: space-between; gap: 8px; }
.mock-sidebar-row span:last-child { color: var(--accent-dark); }

.mock-content { display: flex; flex-direction: column; }
.mock-content-header {
  height: 30px; display: flex; align-items: center; justify-content: space-between; padding: 0 14px;
  border-bottom: 1px solid rgba(244,243,239,.14); color: var(--dark-muted-58);
}
.mock-live { display: flex; align-items: center; gap: 6px; }
.mock-live-dot { width: 6px; height: 6px; border-radius: 50%; background: #00e08a; animation: pulseDot 2.4s ease-in-out infinite; }
.mock-content-body { padding: 16px; display: flex; flex-direction: column; gap: 12px; flex: 1; }
.mock-content-title { font: 500 15px/1.3 var(--font-display); letter-spacing: -.02em; color: var(--dark-ink); }

.mock-stats { display: grid; grid-template-columns: repeat(3,1fr); gap: 8px; font: 400 11px/1.4 var(--font-mono); color: rgba(244,243,239,.85); }
.mock-stat { padding: 10px; border: 1px solid rgba(244,243,239,.18); }
.mock-stat-figure { font: 600 20px/1 var(--font-display); color: var(--accent-dark); }
.mock-stat-label { margin-top: 6px; }

.mock-gradient { flex: 1; min-height: 120px; background: linear-gradient(135deg, rgba(43,43,240,.35), rgba(0,194,255,.18), rgba(0,224,138,.15)); opacity: .5; }

.mock-preview-bar {
  height: 34px; background: rgba(244,243,239,.08); display: flex; align-items: center; gap: 14px; padding: 0 12px;
  font: 500 11px/1 var(--font-mono); color: rgba(244,243,239,.75);
}
.mock-preview-bar.is-accent { height: 28px; background: #0078d4; color: #fff; }
.mock-push { margin-left: auto; }

.mock-caption { margin-top: 16px; color: var(--dark-muted-58); }

/* Results figures */
.figures-row { margin-top: 28px; padding: 40px 0; display: grid; grid-template-columns: repeat(3,1fr); gap: 32px; }
.figure { display: flex; flex-direction: column; gap: 8px; }
.figure .t-label { color: var(--ink-68); }
@media (max-width: 767px) { .figures-row { grid-template-columns: 1fr 1fr; gap: 24px; padding: 32px 0; } }

/* ── Lock overlay (protected case studies) ── */
/* The locked page is just this row - min-height fills the rest of the
   viewport below the 96px nav so the rail-cell's dividing line (which
   stretches to match its row-content sibling's height, same as every
   other rail row) runs to the bottom of the screen instead of stopping
   wherever the gate prompt's own text happens to end. */
[data-locked-content] { min-height: calc(100vh - 96px); }
.lock-gate {
  padding: 64px var(--gutter) 96px; max-width: 640px;
}
.lock-gate .t-sectionhead { margin-bottom: 8px; }
.lock-gate .t-body { color: var(--ink-60); margin-top: 12px; }
.lock-gate-form { margin-top: 28px; display: flex; flex-wrap: wrap; gap: 0; max-width: 480px; }
.lock-gate-form input {
  flex: 1 1 220px; min-width: 0; height: 52px; padding: 0 16px; border: 1px solid rgba(22,22,26,.55); border-right: none;
  background: transparent; color: var(--ink); font: 500 16px/1.35 var(--font-body);
}
.lock-gate-form input:focus-visible { outline-offset: -2px; }
.lock-gate-form button.t-label {
  flex: none; height: 52px; padding: 0 24px; border: 1px solid var(--accent); background: var(--accent); color: var(--bg);
  cursor: pointer; font: 500 11px/1 var(--font-mono); letter-spacing: .16em; text-transform: uppercase;
}
.lock-gate-form button:hover { background: var(--ink); border-color: var(--ink); }
/* Below ~480px the input's flex-basis (220px) plus the button no longer
   fit the row, and relying on flex-wrap to reflow them was landing
   inconsistently (the input rendering wider than its container on some
   mobile browsers instead of shrinking). Stacking them explicitly instead
   is deterministic regardless of that quirk - the input already stretches
   to fill the column via flex's default align-items:stretch, so no width
   rule is needed here, just its right border back since it's no longer
   flush against the button. */
@media (max-width: 479px) {
  .lock-gate-form { flex-direction: column; max-width: 400px; }
  /* flex-direction:column makes the main axis vertical, so the input's
     flex-basis (220px) would otherwise apply to its height instead of
     its width, stretching it to fill the column - flex:none drops that,
     leaving its own explicit height:52px and full-width stretch. */
  .lock-gate-form input { flex: none; border-right: 1px solid rgba(22,22,26,.55); }
  .lock-gate-form button.t-label { margin-top: 12px; }
}
.lock-gate-msg { margin-top: 14px; min-height: 1.4em; color: var(--ink-68); font: 400 13px/1.6 var(--font-mono); }
.lock-gate-msg.is-error { color: #c0362c; }
.lock-gate-msg.is-success { color: var(--accent); }
.locked-fields[hidden], .unlocked-fields[hidden] { display: none; }

/* ── Tool spotlight (e.g. "In focus" — one live tool from a portfolio) ── */
.tool-spotlight { margin-top: 40px; display: grid; grid-template-columns: minmax(0,3fr) minmax(0,5fr); gap: 64px; }
.tool-link {
  margin-top: 24px; display: inline-flex; align-items: center; gap: 10px; height: 48px; padding: 0 18px;
  border: 1px solid rgba(22,22,26,.3); color: var(--ink); text-decoration: none;
  font: 500 11px/1 var(--font-mono); letter-spacing: .16em; text-transform: uppercase;
  transition: background .2s, color .2s, border-color .2s;
}
.tool-link span { color: var(--accent); transition: color .2s; }
.tool-link:hover { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.tool-link:hover span { color: var(--bg); }
.tool-url { margin-top: 12px; color: var(--ink-68); }
/* Full-height screenshot of the spotlighted tool, in the right column.
   Thin frame so the page's white edges don't melt into the background. */
.tool-shot { margin: 0; }
.tool-shot img { display: block; width: 100%; height: auto; background: #fff; border: 1px solid rgba(120,120,140,.25); }
.detail-grid { margin-top: 46px; display: grid; grid-template-columns: 1fr 1fr; gap: 24px 32px; }
.detail-item { padding-top: 18px; box-shadow: inset 0 1px 0 var(--ink-18); }
.detail-item p { margin-top: 10px; color: var(--ink-60); }
@media (max-width: 767px) {
  .tool-spotlight { grid-template-columns: 1fr; gap: 32px; }
  .detail-grid { grid-template-columns: 1fr; }
}

/* ── Prev / next footer ── */
.case-pager .row-content { box-shadow: inset 0 1px 0 var(--ink-18); }
.pager-content {
  display: grid; grid-template-columns: minmax(0,4fr) minmax(0,1fr) minmax(0,4fr); align-items: center; padding: 40px 0;
}
.pager-link { text-decoration: none; color: var(--ink); display: flex; flex-direction: column; gap: 10px; }
.pager-next { text-align: right; align-items: flex-end; }
.pager-link .t-label { color: var(--ink-68); transition: color .25s; }
.pager-link:hover .t-label { color: var(--accent); }
.pager-link .t-navtitle { color: var(--ink); transition: color .25s; }
.pager-link:hover .t-navtitle { color: var(--accent); text-decoration: underline; }
.pager-connector { display: flex; align-items: center; }
.pager-connector .line { flex: 1; border-top: 1px dashed var(--ink-18); }
.pager-connector .diamond { width: 11px; height: 11px; background: var(--accent); transform: rotate(45deg); flex: none; }
@media (max-width: 767px) {
  .pager-content { grid-template-columns: minmax(0,1fr); gap: 24px; }
  .pager-next { text-align: left; align-items: flex-start; }
  .pager-connector { display: none; }
}

/* ── Image lightbox (see case-study.js) ──
   The dialog itself is the full-viewport dark backdrop (rather than
   ::backdrop, whose transitions are unevenly supported), so one opacity
   fade covers both it and the image. */
[data-zoomable] { cursor: zoom-in; }
[data-zoomable]:focus-visible { outline: 2px solid var(--accent-focus); outline-offset: 3px; }
html.has-lightbox { overflow: hidden; }
.lightbox {
  position: fixed; inset: 0; width: 100vw; height: 100vh; max-width: none; max-height: none;
  margin: 0; padding: 24px; border: 0; box-sizing: border-box;
  background: rgba(10,10,14,.9); color: #fff; cursor: zoom-out;
  opacity: 0; transition: opacity .16s ease;
}
.lightbox[open] { display: flex; align-items: center; justify-content: center; }
.lightbox::backdrop { background: transparent; }
.lightbox.is-open { opacity: 1; }
.lightbox-img {
  display: block; max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain;
  transform: scale(.97); transition: transform .2s cubic-bezier(.22,.61,.36,1);
}
.lightbox.is-open .lightbox-img { transform: none; }
.lightbox-close {
  position: absolute; top: 12px; right: 12px; z-index: 1; width: 44px; height: 44px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; color: rgba(255,255,255,.75); cursor: pointer; transition: color .2s;
}
.lightbox-close:hover { color: #fff; }
.lightbox-close:focus-visible { outline: 2px solid var(--accent-dark); outline-offset: -4px; }
