/* ============================================================
   case-study.css — shared layout for case-study write-ups
   (scifinder.html, and analytical-methods.html / formulus.html
   when they're written the same way).

   Nothing here hard-codes a colour or a face — same rule as
   site.css. This file only adds the article-level layout those
   pages need on top of tokens.css + site.css: the section rail,
   the responsive text columns, and the two image-collage patterns.

   To build the next case study: copy scifinder.html's structure
   (header → <main><article> → .case-section per top-level heading
   → .case-subsection per h3, with .case-editorial wrapping the
   heading + copy and .case-figure or .case-sketch-grid around the
   art), swap the content, done. Nothing here is scifinder-specific.
   ============================================================ */

/* --- skip link -----------------------------------------------
   The site had none. Visually hidden until focused, first thing
   in the body so it's the first stop for keyboard and screen-reader
   users. */
.skip-link{
  position:absolute;top:-3px;left:1rem;
  transform:translateY(-120%);
  background:var(--ground);
  color:var(--ink);
  padding:.6rem 1rem;
  border-radius:0 0 8px 8px;
  font-family:var(--ui);
  font-size:.95rem;
  z-index:20;
  transition:transform .15s ease;
}
.skip-link:focus{transform:translateY(0);}
@media (prefers-reduced-motion:reduce){
  .skip-link{transition:none;}
}

/* --- header ---------------------------------------------------- */
/* .case-title lives in site.css (shared with the other stub pages), so
   this overrides its colour rather than editing that file directly —
   case-study.css loads after it. .case-meta ("cas") is no longer used
   on this page: Company is now one of the three info cards below. */
.case-title{color:var(--accent);}

.case-back{
  font-family:var(--ui);
  font-size:.95rem;
  display:inline-block;
  margin-bottom:1rem;
}

/* One-line bold summary under the title. Near-black, not accent —
   matches the reference: the coloured title carries the accent, the
   descriptive line under it reads as body-weight-but-bold, not a
   second heading. */
.case-lead{
  font-family:var(--display-alt);
  font-weight:400;
  color:var(--ink);
  font-size:clamp(1.25rem,2.4vw,1.375rem);   /* ~20-22px */
  line-height:1.3;
  margin:.6rem 0 0;
  max-width:60ch;
}

/* Role / Company / Toolkit as a plain row, no card chrome — thin
   vertical rules between items instead of boxes. Labels use the same
   bold display face as the page's other headings (never the hairline
   "Harry Thin" label face this page used to lean on for small text —
   see .case-nav below for why that was a real legibility bug, not
   just a colour one), sized well under the heading scale so they read
   as labels, not a fourth heading level. */
.case-facts{
  display:flex;flex-wrap:wrap;
  margin:1.75rem 0 0;
  padding:0;
}
.case-facts > div{
  padding:0 1.75rem;
  border-left:1px solid var(--rule);
  display:flex;flex-direction:column;gap:.3rem;
}
.case-facts > div:first-child{padding-left:0;border-left:none;}
.case-facts dt{
  font-family:var(--display-alt);
  font-weight:400;
  font-size:1.0625rem;    /* ~17px — bold face, never thin, but a label, not a heading */
  line-height:1.2;
  color:var(--accent);
  margin:0;
}
.case-facts dd{
  font-family:var(--body-text);
  font-size:1.05rem;
  line-height:1.4;
  letter-spacing:.022em;         /* condensed type needs the tracking — same as .info-card p */
  color:var(--ink);
  margin:0;
}
@media (max-width:46rem){
  .case-facts{flex-direction:column;gap:1.1rem;}
  .case-facts > div{
    padding:1.1rem 0 0;
    border-left:none;
    border-top:1px solid var(--rule);
  }
  .case-facts > div:first-child{padding-top:0;border-top:none;}
}

/* The overview paragraph used to stop at 75ch with nothing telling the
   reader why, leaving an awkward empty right side under the wider cards
   above it. Heading added; the paragraph is now capped at 1100px, same
   as .case-figure and .case-editorial below — one consistent right edge
   running down the whole page, rather than trailing off at a width
   that lined up with nothing. */
.case-overview{margin:clamp(2rem,4vw,3rem) 0 0;}
.case-overview p{
  font-family:var(--body-text);
  font-size:1.25rem;    /* ~20px, this page's one body size */
  line-height:1.6;
  letter-spacing:.022em;
  color:var(--ink);
  max-width:1100px;
  margin:1rem 0 0;
}

/* In-page jump nav to the three top-level sections — plain text
   links, no pill/chip background. The number is just the first word
   of the link text (not a separate badge element): "1 Software
   Homepages" reads and announces naturally as one link.

   header.sheet .wrap carries the base .wrap's own calc(--step*2)
   bottom padding (site.css — meant for a simple single-wrap page),
   which was stacking under the first .case-section's own padding-top
   below it: a second, unrelated ~250px gap of the same kind the
   sections themselves had. Zeroed here; margin-bottom below is the
   nav's own (smaller, responsive) share of that gap instead. */
header.sheet .wrap{padding-bottom:0;}
.case-nav{
  display:flex;flex-wrap:wrap;align-items:center;
  gap:.25rem 1.5rem;
  margin:1.5rem 0 clamp(1.25rem,3vw,1.5rem);
  padding-bottom:1.5rem;
  border-bottom:1px solid var(--rule);
}
/* "Jump to" is what says this row is navigation. The label sits closer
   to the links than the links do to each other, so it reads as their
   caption. Links stay underlined at rest: colour alone is not a link
   signal. */
.case-nav-label{
  font-family:var(--ui);
  font-size:1rem;
  font-weight:500;
  color:var(--ink);
  margin-right:-.75rem;
}
.case-nav a{
  font-family:var(--ui);
  font-size:1rem;          /* 16px, comfortably over the 15px floor */
  font-weight:500;
  color:var(--accent);
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:.25em;
  padding:.5rem 0;
}
.case-nav a:hover,.case-nav a:focus-visible{
  text-decoration-thickness:2px;
}
@media (max-width:46rem){
  .case-nav{flex-direction:column;align-items:flex-start;gap:0;}
  .case-nav-label{margin-right:0;padding:.25rem 0 .1rem;}
  .case-nav a{width:100%;padding:.75rem 0;}   /* ~44px tall on touch */
}

/* --- section rhythm -----------------------------------------------
   Was a static border-left "rail" here, styled after the timeline's
   scroll-filled one. Dropped: .case-section spans full viewport width
   with no inset of its own, so the border sat at the true window edge
   (x:0) while .wrap--wide's content centers and insets independently —
   on anything wider than the 96rem wrap, or narrower than it, the line
   ran disconnected from the column it was meant to mark, broken into
   per-section segments with gaps between them rather than one
   continuous mark. Reproducing the timeline's real behaviour (rail
   aligned to content, scroll-linked) isn't worth it for a decoration;
   a plain divider between sections does the "new chapter" job instead.

   .wrap itself (site.css) already carries calc(--step*2) --step of its
   OWN vertical padding — stacking that under this section's padding
   below was the ~320px gaps between H2s: two paddings nested with
   nothing between them to explain why. Zeroing the wrap's here makes
   this section's padding the only vertical rhythm in play.

   The extra padding-left below reserves a lane for the number badge —
   see "numbered sections" further down. It's added here rather than as
   a badge-side negative margin so the reserved space is real layout,
   not an overhang that could clip against the wrap's own edge on a
   narrow screen. */
.case-section{
  padding:clamp(2.25rem,4vw,3.25rem) 0;
}
.case-section + .case-section{border-top:1px solid var(--rule);}
.case-section > .wrap{
  --case-badge:clamp(34px,4.2vw,48px);
  --case-badge-gap:1rem;
  position:relative;
  padding-top:0;padding-bottom:0;
  padding-left:calc(var(--step) + var(--case-badge) + var(--case-badge-gap));
}
@media (max-width:46rem){
  .case-section > .wrap{--case-badge:32px;--case-badge-gap:.65rem;}
}

/* Shares its size/colour with the header's "Overview" heading — one
   H2 scale for the whole page. Green, per this page's rule that
   headings and accents stay green while body copy goes near-black. */
.case-overview h2,.case-section h2{
  font-family:var(--display-alt);
  font-weight:400;
  color:var(--accent);
  font-size:clamp(1.75rem,4.5vw,3rem);        /* ~28-48px */
  line-height:.95;
  margin:0 0 clamp(1.25rem,2.5vw,2rem);
}

/* --- numbered sections ---------------------------------------------
   A filled circle badge (1/2/3) sits in the lane reserved above, next
   to each H2, with a line running from it down through the rest of
   that section — a static echo of the home page's scroll-filled
   timeline rail, not a rebuild of it (this page has three fixed
   chapters, not a scroll-driven list, so there's nothing to animate).

   Both the badge and the line are positioned from .case-section's own
   .wrap, the same element every other line of content in the section
   already aligns to — not from the section's full-bleed outer edge.
   That's the fix for the previous rail: anchoring to an element whose
   left edge moves with the centred content, instead of one that stays
   at the true viewport edge regardless of where the content actually
   sits.

   The badge is aria-hidden; the H2 text right next to it is already a
   complete, accessible section name on its own. */
.case-section-head{position:relative;}
.case-badge{
  position:absolute;
  left:calc(-1 * (var(--case-badge) + var(--case-badge-gap)));
  top:50%;
  transform:translateY(-50%);
  width:var(--case-badge);height:var(--case-badge);
  border-radius:50%;
  background:var(--accent);
  color:var(--ground);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--display-alt);
  font-weight:400;
  font-size:calc(var(--case-badge) * .42);
  line-height:1;
}
/* .case-rail-line is a sibling of .case-section-head, not a child of
   it — it needs to reach bottom:0 against .wrap itself to stretch the
   full section, which .case-section-head (just the heading row) can't
   give it. So unlike .case-badge above, its left is measured from
   .wrap's own edge, not from where the badge sits within the heading
   row: var(--step) is where the badge's left edge lands (the section's
   padding-left minus the badge+gap it reserved), so the line's centre
   is var(--step) plus half the badge. */
.case-rail-line{
  position:absolute;
  left:calc(var(--step) + var(--case-badge)/2 - 1px);
  top:var(--case-badge);
  bottom:0;
  width:2px;
  background:color-mix(in srgb,var(--accent) 25%,transparent);
}

.case-subsection{margin-top:clamp(2rem,4vw,3rem);}
.case-subsection:first-child{margin-top:0;}
.case-subsection h3{
  font-family:var(--display-alt);
  font-weight:400;
  font-size:clamp(1.5rem,3vw,2rem);           /* ~24-32px */
  line-height:1.05;
  margin:0 0 1rem;
  color:var(--accent);
}

/* --- editorial layout ---------------------------------------------
   Was a heading-left/copy-right grid (30/70). Dropped: whenever the
   copy ran shorter than the heading's row height (or vice versa), the
   short side left a bare rectangle of empty space next to or under
   the other — visible on "Changing the Structure" and "The New" in
   particular. One column now, top to bottom: heading, lead sentence,
   body/bullets, then image or slider — nothing beside anything, so
   there's no mismatched height left to show up as a gap.

   1100px, not a ch cap: that's .case-figure's own max-width too, so a
   text block's right edge lands exactly on an image's right edge
   below it — the same reasoning as before, just a wider number (this
   page reads fine at ~19-20px body across the full measure, which
   960px was slightly cramping). */
.case-editorial{
  display:flex;flex-direction:column;
  gap:.85rem;
  max-width:1100px;
  margin-top:1rem;
}
.case-editorial h3{margin:0;}
.case-editorial-content{
  display:flex;flex-direction:column;
  gap:1em;                      /* one consistent paragraph/lead/list gap, not mixed margins */
}
/* Scoped as .case-editorial-content .case-lead-sentence (2 classes),
   not just .case-lead-sentence alone — the lead is itself a <p>, so
   the plain-paragraph rule right below it (1 class + element) would
   otherwise outrank a single-class selector and silently override
   its font-size back down to body size. */
.case-editorial-content .case-lead-sentence{
  font-family:var(--display-alt);
  font-weight:400;
  font-size:clamp(1.3rem,2.2vw,1.5rem);   /* ~21-24px, a clear step above the ~20px body below */
  line-height:1.3;
  color:var(--ink);
  margin:0;
}
.case-editorial-content p{
  margin:0;
  font-family:var(--body-text);
  font-size:1.25rem;       /* ~20px — full-width lines need the extra size to stay comfortable */
  line-height:1.6;
  letter-spacing:.022em;         /* condensed type needs the tracking — same as .info-card p */
  color:var(--ink);              /* was inheriting site.css's global p{color:var(--muted)} —
                                     every plain <p> on the page was rendering green, not the
                                     near-black body colour the rest of the type scale calls for */
}
.case-editorial-content ul{
  margin:0;
  padding-left:1.15em;
  display:flex;flex-direction:column;
  gap:.6em;
}
.case-editorial-content li{
  font-family:var(--body-text);
  font-size:1.25rem;
  line-height:1.5;
  letter-spacing:.022em;
  color:var(--ink);
  padding-left:.2em;
}
.case-editorial-content li::marker{color:var(--accent);}

/* Marks a paragraph that hasn't been written yet, per Ronny's
   note — visible in dev so it isn't mistaken for finished copy,
   never shipped silently as blank space. */
.case-todo{
  border:1px dashed var(--rule);
  border-radius:8px;
  padding:1rem 1.2rem;
  font-family:var(--ui);
  font-size:.92rem;
  color:var(--muted);
  font-style:italic;
}

/* --- images ------------------------------------------------------- */
/* Left-aligned, not centred: a centred 1100px image inside a wrap
   wider than 1100px doesn't share a right edge with left-aligned text
   above it (their gaps to the wrap's right edge differ). Every
   1100px-capped block on this page — this, .case-sketch-grid,
   .compare-slider, .case-editorial, .case-stats — stays flush left
   instead, so they all share both edges at every viewport width. */
.case-figure{
  margin:2rem 0 0;
  max-width:1100px;
  line-height:0;               /* kills the descender gap under inline img */
}
.case-figure img{
  width:100%;height:auto;
  display:block;
  border-radius:clamp(12px,1.6vw,20px);
  box-shadow:var(--shadow-card-lift);
}

/* --- process flowchart (inline SVG) --------------------------------
   Was a photo of a hand-drawn flowchart, illegible at any width that
   fit the page. Rebuilt as a diagram in code instead, sized so its
   labels read at a real 14px+ when the SVG is shown at its own fixed
   width. Widening the column gaps and giving Top Nav Bar room to sit
   as its own node (rather than being squeezed between Account and
   Search) pushed that fixed width to 1400px — past the page's own
   1100px content cap, so on every viewport this now genuinely needs
   its own horizontal scroll rather than fitting free on wide desktops
   the way the narrower first draft did. That's a fair trade for the
   spacing this needed.

   Because "does it need to scroll" no longer maps to one CSS
   breakpoint (it's true almost everywhere now), the hint is toggled
   by actual overflow measurement in JS (see stat-counter.js) rather
   than a media query guessing at it (see lightbox.js) — a query would
   either show a stale hint on the rare viewport where it fits, or
   hide a needed one. */
.case-flowchart-wrap{
  margin-top:2rem;
  max-width:1100px;
  overflow-x:auto;
  -webkit-overflow-scrolling:touch;
  border-radius:clamp(12px,1.6vw,20px);
  background:var(--panel);
  box-shadow:var(--shadow-card-lift);
}
.case-flowchart{
  display:block;
  width:1400px;         /* fixed on purpose — see above */
  height:auto;
  padding:1.75rem clamp(1rem,3vw,1.75rem);
}
.fc-edge{fill:none;stroke:var(--forest-500);stroke-width:2;}
.fc-node-terminal{fill:var(--forest-900);stroke:var(--forest-900);stroke-width:1.5;}
.fc-node-step{fill:var(--forest-100);stroke:var(--forest-400);stroke-width:1.5;}
.fc-node-ref{fill:var(--forest-50);stroke:var(--forest-300);stroke-width:1.5;stroke-dasharray:4 3;}
.fc-label-light{font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;font-size:14px;fill:var(--apricot-50);}
.fc-label-dark{font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;font-size:14px;fill:var(--ink);}
.fc-label-sub{font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;font-size:14px;fill:var(--muted);}

.case-flowchart-hint{
  display:none;
  margin:.6rem 0 0;
  font-family:var(--ui);
  font-size:.85rem;
  color:var(--muted);
  text-align:center;
}
.case-flowchart-hint.is-visible{display:block;}

/* --- sketch grid + lightbox -----------------------------------------
   What's left after the flowchart moved to its own diagram above is
   just the hand-drawn sketches, framed to one shared landscape ratio — some
   were photographed portrait (rotate them and the ratio already
   matches; see the *-landscape.jpg files, saved alongside the
   untouched originals), some were already landscape, so contain
   (not cover) keeps every sketch whole either way, letterboxed on
   .panel rather than cropped. */
.case-sketch-grid{
  display:grid;
  grid-template-columns:repeat(2,1fr);
  gap:1rem;
  margin-top:2rem;
  max-width:1100px;
}
/* An odd item count leaves the last cell alone in its row — centre it
   at half width instead of letting it stretch full-width lopsided. */
.case-sketch-grid > :last-child:nth-child(odd){
  grid-column:1 / -1;
  max-width:calc(50% - .5rem);
  margin-inline:auto;
}
@media (max-width:46rem){
  .case-sketch-grid{grid-template-columns:1fr;}
  .case-sketch-grid > :last-child:nth-child(odd){max-width:none;}
}
/* Two full-size screenshots side by side, each kept at its own natural
   aspect ratio (tall pages would be cropped by .case-sketch-grid's fixed
   frame). align-items:start lets two different heights sit top-aligned. */
.case-figure-pair{
  display:grid;
  grid-template-columns:repeat(2,1fr);
  gap:1rem;
  align-items:start;
  margin-top:2rem;
  max-width:1100px;
}
.case-figure-pair .case-figure{margin-top:0;}
@media (max-width:46rem){
  .case-figure-pair{grid-template-columns:1fr;}
}
.case-sketch-grid figure{
  margin:0;
  aspect-ratio:933/700;
  background:var(--panel);
  border-radius:clamp(10px,1.4vw,16px);
  box-shadow:var(--shadow-card);
  overflow:hidden;
}

/* A small heading, the same level as .case-subsection h3 but a step
   down — for a label inside a subsection (like "Low-fidelity
   wireframes") that isn't itself a new subsection. Was an italic
   caption-style line; that read as a caption for the images below it,
   not as a heading a screen-reader user could jump to. */
.case-subheading{
  margin:2rem 0 .5rem;
  font-family:var(--display-alt);
  font-weight:400;
  font-size:clamp(1.15rem,2vw,1.375rem);
  line-height:1.15;
  color:var(--accent);
}

/* Digitized low-fi screens, presented like frames on a Figma canvas:
   a light-gray canvas panel, a small gray frame label above each
   artboard (outside the artboard itself, the way Figma's own frame
   names sit in the canvas chrome, not baked into the export), and the
   artboard itself square-cornered and white — deliberately NOT this
   page's usual rounded-card treatment, since the point is to read as
   an exported frame, not a site component. */
.case-artboard-canvas{
  margin-top:1rem;
  max-width:1100px;
  padding:clamp(1.25rem,3vw,2rem);
  background:#D9D9D9;
  border-radius:clamp(10px,1.4vw,16px);
}
.case-artboard-grid{
  display:grid;
  grid-template-columns:repeat(2,1fr);
  gap:clamp(1.25rem,3vw,2rem);
}
.case-artboard{
  display:flex;flex-direction:column;gap:.5rem;
}
.case-artboard-label{
  margin:0;
  font-family:var(--ui);
  font-size:.875rem;    /* 14px floor, same as everywhere else on this page */
  color:#5A5A5A;        /* 4.89:1 on the #D9D9D9 canvas — clears AA */
}
.case-artboard figure{
  margin:0;
  aspect-ratio:800/1000;
  background:#FFFFFF;
  box-shadow:0 1px 4px rgba(0,0,0,.18);
  overflow:hidden;
}
/* Landscape artboards, for pages whose screens are wider than tall (the
   portrait 4:5 default suits SciFinder's homepage wireframes). --wide runs
   the full grid width for a screen that needs the room. */
.case-artboard-grid--landscape .case-artboard figure{aspect-ratio:4/3;}
.case-artboard-grid--landscape .case-artboard.case-artboard--wide{grid-column:1/-1;}
.case-artboard-grid--landscape .case-artboard.case-artboard--wide figure{aspect-ratio:5/3;}
@media (max-width:46rem){
  .case-artboard-grid{grid-template-columns:1fr;}
}
.case-zoom-trigger{
  all:unset;
  display:block;
  width:100%;height:100%;
  cursor:zoom-in;
  position:relative;
}
.case-zoom-trigger img{
  width:100%;height:100%;
  object-fit:contain;
  display:block;
}
.case-zoom-hint{
  position:absolute;right:.6rem;bottom:.6rem;
  width:2rem;height:2rem;
  border-radius:50%;
  background:var(--forest-900);
  color:var(--apricot-50);
  display:flex;align-items:center;justify-content:center;
  opacity:0;
  transition:opacity .15s ease;
  pointer-events:none;
}
.case-zoom-hint svg{width:55%;height:55%;}
.case-zoom-trigger:hover .case-zoom-hint,
.case-zoom-trigger:focus-visible .case-zoom-hint{opacity:1;}
.case-zoom-trigger:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px;}

/* The flowchart's own zoom trigger — reuses the same button pattern,
   but .case-figure has no fixed aspect-ratio/height (it sizes to the
   image's own, nearly-square, shape), so the grid's width:100%;
   height:100% trigger sizing doesn't apply here — height:auto instead,
   matching how a plain .case-figure img already behaves. */
.case-figure .case-zoom-trigger{height:auto;cursor:zoom-in;}
.case-figure .case-zoom-trigger img{
  width:100%;height:auto;
  border-radius:clamp(12px,1.6vw,20px);
  box-shadow:var(--shadow-card-lift);
}

/* One shared dialog, reused for every zoomable image on the page —
   lightbox.js swaps its image/caption per click rather than building
   a dialog per figure. Native <dialog> gives focus trapping and
   Esc-to-close for free; the script only handles returning focus to
   whichever trigger opened it. */
.case-lightbox{
  border:none;
  padding:0;
  /* Width follows the image (lightbox.js sets --lb-w from its natural size), capped
     to the viewport, so a tall page opens at a readable width and scrolls inside
     the box instead of being shrunk to fit the screen height. */
  width:min(var(--lb-w,1100px),94vw,1400px);
  max-width:none;
  max-height:92vh;
  overflow-y:auto;
  overscroll-behavior:contain;
  background:var(--ground);
  border-radius:clamp(12px,1.6vw,20px);
  box-shadow:var(--shadow-card-lift);
}
.case-lightbox::backdrop{background:var(--scrim);}
.case-lightbox img{
  display:block;
  width:100%;
  height:auto;
  background:var(--panel);
}
.case-lightbox-caption{
  margin:0;
  padding:1rem 1.5rem 1.25rem;
  font-family:var(--ui);
  font-size:.95rem;
  color:var(--ink);
}
.case-lightbox-close{
  /* Sticky keeps the button in view while a tall image scrolls under it; the
     negative bottom margin cancels its own height so it takes no space above
     the image (.75rem + 2.25rem = 3rem). */
  position:sticky;top:.75rem;
  margin:.75rem .75rem -3rem auto;
  z-index:1;
  width:2.25rem;height:2.25rem;
  border-radius:50%;
  background:var(--ground);
  border:1px solid var(--rule);
  color:var(--ink);
  display:flex;align-items:center;justify-content:center;
  cursor:pointer;
}
.case-lightbox-close svg{width:60%;height:60%;}
.case-lightbox-close:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px;}

/* Branding backgrounds: the source stacks these full-width, one
   per product, and there's no reason to force them into a grid
   they were never designed for. */
.case-branding{display:flex;flex-direction:column;gap:2rem;margin-top:2rem;}
.case-branding .case-figure{margin-top:0;}   /* the flex gap above already spaces these; .case-figure's own margin-top would double it */

/* --- before/after comparison slider ------------------------------
   One native <input type="range"> does all the work: it already
   handles mouse drag, touch drag, and keyboard (arrows, Home/End,
   PageUp/Down) with no extra JS. The input sits full-size and
   fully transparent on top of everything (z-index:3), so it is
   what actually receives pointer and keyboard input; the visible
   line-and-knob handle underneath it is aria-hidden and purely
   decorative, positioned from the same --split value.

   js/compare-slider.js only ever does one thing: copy the input's
   value onto --split. Everything else — the clip-path reveal, the
   handle position — is this stylesheet reading that one variable.

   Reused for three comparisons on this page (homepage, all page,
   multicomponent), each pairing two screenshots that are genuinely
   different shapes — object-fit:cover would crop one of them to fit
   a shared box. contain keeps both whole and unstretched; --slider-bg
   fills the resulting letterbox so it reads as padding, not a gap,
   matching the screenshots' own light chrome. One fixed aspect-ratio
   for all three keeps the component actually reusable rather than
   re-tuned per instance. */
.compare-slider{
  --slider-bg:var(--panel);
  position:relative;
  aspect-ratio:8/5;
  width:100%;
  max-width:1100px;
  margin:2rem 0 0;
  overflow:hidden;
  border-radius:clamp(12px,1.6vw,20px);
  background:var(--slider-bg);
  box-shadow:var(--shadow-card-lift);
  touch-action:none;          /* the drag is horizontal; this stops it also panning the page */
  -webkit-user-select:none;user-select:none;
}
.compare-slider img{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:contain;
  object-position:top;
  display:block;
  pointer-events:none;        /* the range input above them takes all input */
}
/* No transition on either of these: --split is rewritten on every
   'input' event during a drag, and a transition on a value rewritten
   that often only ever lags behind the pointer rather than smoothing
   anything — the same reasoning about.js documents for --about-progress. */
.compare-overlay{
  background:var(--slider-bg);   /* the contained image rarely fills the frame; without this the letterbox is transparent and the new version's bottom shows through beneath it */
  clip-path:polygon(0 0,var(--split) 0,var(--split) 100%,0 100%);
}
.compare-handle{
  position:absolute;top:0;bottom:0;
  left:var(--split);
  transform:translateX(-50%);
  display:flex;align-items:center;justify-content:center;
  pointer-events:none;
}
.compare-line{
  position:absolute;top:0;bottom:0;left:50%;
  width:3px;margin-left:-1.5px;
  background:var(--panel);
  box-shadow:0 0 0 1px rgba(21,50,26,.25);
}
.compare-knob{
  position:relative;
  width:clamp(34px,5vw,44px);height:clamp(34px,5vw,44px);
  border-radius:50%;
  background:var(--forest-700);
  color:var(--apricot-50);
  display:flex;align-items:center;justify-content:center;
  box-shadow:var(--shadow-card-lift);
}
.compare-knob svg{width:55%;height:55%;}
.compare-label{
  position:absolute;top:clamp(.6rem,1.6vw,1rem);
  font-family:var(--ui);      /* was "Harry Thin" — the same hairline face
                                  behind the ROLE/TOOLKIT/jump-nav legibility
                                  issue elsewhere on this page */
  font-size:.875rem;          /* 14px floor */
  color:var(--apricot-50);
  background:var(--forest-900);
  padding:.35rem .7rem;
  border-radius:999px;
  pointer-events:none;
}
.compare-label-before{left:clamp(.6rem,1.6vw,1rem);}
.compare-label-after{right:clamp(.6rem,1.6vw,1rem);}
.compare-caption{
  margin:.85rem 0 0;
  max-width:1100px;
  font-family:var(--ui);
  font-size:.92rem;
  font-style:italic;
  color:var(--muted);
  text-align:center;
}
.compare-input{
  position:absolute;inset:0;
  width:100%;height:100%;
  margin:0;
  opacity:0;
  cursor:ew-resize;
  -webkit-appearance:none;appearance:none;
  z-index:3;
}
.compare-input::-webkit-slider-thumb{-webkit-appearance:none;width:1px;height:1px;}
.compare-input::-moz-range-thumb{width:1px;height:1px;opacity:0;}
/* The knob is what reads as the control, but it's aria-hidden and
   pointer-events:none — so the focus ring has to render on it via
   the input's OWN focus state, using :has() rather than adjacency,
   since nothing about DOM order should matter here. */
.compare-slider:has(.compare-input:focus-visible) .compare-knob{
  outline:2px solid var(--focus-ring);
  outline-offset:2px;
}

/* --- stat callouts ------------------------------------------------
   A compact band, not cards: a rule above and below, one thin divider
   between the two stats, number and label side by side rather than
   stacked in a mostly-empty box. */
.case-stats-intro{
  margin:2rem 0 0;
  max-width:1100px;
  font-family:var(--body-text);
  font-size:1.25rem;
  line-height:1.6;
  letter-spacing:.022em;
  color:var(--ink);
}
.case-stats{
  display:grid;
  grid-template-columns:repeat(2,1fr);
  max-width:1100px;
  margin-top:1.5rem;
  border-top:1px solid var(--rule);
  border-bottom:1px solid var(--rule);
}
.case-stat{
  display:flex;align-items:center;gap:1.25rem;
  padding:1.25rem 1.75rem;
}
.case-stat:first-child{padding-left:0;}
.case-stat + .case-stat{border-left:1px solid var(--rule);}
.case-stat-number{
  flex:0 0 auto;
  font-family:var(--display-alt);
  font-weight:400;
  font-size:clamp(2rem,4vw,2.75rem);
  line-height:1;
  color:var(--forest-700);
  margin:0;
}
.case-stat-label{
  margin:0;
  font-family:var(--body-text);
  color:var(--ink);          /* was --muted (forest-700); at this size/weight it
                                 already cleared AA nominally, but the same
                                 anti-aliasing thinning as the eyebrow labels
                                 above was pulling the rendered text well
                                 under the 4.5:1 bar. */
  font-size:.95rem;
  line-height:1.4;
  letter-spacing:.022em;
}
@media (max-width:46rem){
  .case-stats{grid-template-columns:1fr;}
  .case-stat{padding:1.1rem 0;}
  .case-stat:first-child{padding-left:0;}
  .case-stat + .case-stat{border-left:none;border-top:1px solid var(--rule);}
}

/* Entrance state is added by JS only (stat-counter.js), never present
   by default — so with JS disabled, or before it runs, the cards and
   numbers simply render in their final state straight from the HTML.
   Same rule about.js documents for --about-progress: the script may
   only ever lower what CSS already shows correctly on its own. */
.case-stat{
  transition:opacity .4s ease, transform .7s cubic-bezier(.34,1.56,.64,1);   /* the card springs in */
}
.case-stat.is-pending{opacity:0;transform:translateY(28px) scale(.96);}
.case-stat.is-in{opacity:1;transform:none;}
.case-stat:nth-child(2){transition-delay:.12s;}

/* the number pops when the count lands, and throws a few sparks in the page's own colours */
.case-stat-number{position:relative;display:inline-block;transform-origin:left center;}
.case-stat-number.is-popping{animation:stat-pop .6s cubic-bezier(.34,1.56,.64,1);}
@keyframes stat-pop{
  0%{transform:scale(1) rotate(0);}
  35%{transform:scale(1.22) rotate(-3deg);}
  70%{transform:scale(.97) rotate(1deg);}
  100%{transform:scale(1) rotate(0);}
}
.stat-spark{
  position:absolute;left:.5em;top:50%;
  width:.32em;height:.32em;border-radius:50%;
  background:var(--highlight);
  pointer-events:none;
  animation:stat-spark .8s ease-out forwards;
}
.stat-spark:nth-child(3n+2){background:var(--accent);}
.stat-spark:nth-child(3n){background:var(--apricot-300);width:.22em;height:.22em;}
@keyframes stat-spark{
  0%{transform:translate(-50%,-50%) scale(.4);opacity:1;}
  100%{transform:translate(calc(-50% + var(--dx)),calc(-50% + var(--dy))) scale(1);opacity:0;}
}

.sr-only{
  position:absolute;width:1px;height:1px;
  padding:0;margin:-1px;
  overflow:hidden;
  clip:rect(0,0,0,0);
  white-space:nowrap;
  border:0;
}

@media (prefers-reduced-motion:reduce){
  .case-figure img,.case-sketch-grid img{transition:none;}
  .case-stat{transition:none;}
  .case-stat-number.is-popping,.stat-spark{animation:none;}
}
