/* ==========================================================================
   Figure  (.pf-figure)
   Full-width case-study image with optional caption and optional colored "mat"
   (the blue / green / yellow / pink backdrops behind device shots in the Figma).
   Set the mat color inline:  style="--pf-mat: #2f80ed"
   ========================================================================== */

.pf-figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--pf-space-2);
  /* Media breaks out wider than the 776px reading column it sits in, up to
     --pf-measure-wide, centered on the reading axis. The column is centered in
     the viewport (.pf-measure margin-inline:auto), so centering on its axis =
     centering in the viewport. Width is capped at the viewport minus gutters so
     the break-out never causes horizontal scroll; the cap keeps it inside the
     1200 frame (R12). translateX is a static transform, not motion (no R6 pair). */
  width: min(var(--pf-measure-wide), calc(100vw - 2 * var(--pf-gutter)));
  margin-left: 50%;
  transform: translateX(-50%);
}

.pf-figure__img {
  width: 100%;
  height: auto;
  display: block;
  /* Center any media narrower than the break-out frame (e.g. a schematic capped
     below --pf-measure-wide). Without this, a sub-width image hugs the left. */
  margin-inline: auto;
  border-radius: var(--pf-radius-none); /* sharp, editorial */
  background: var(--pf-surface-sunken);
}

/* Schematic / line-diagram figures (the "supporting" tier of the figure-width
   ladder). Raster screenshots fill the break-out (--pf-measure-wide, 1040); a
   vector schematic reads better contained and centered, so it caps at
   --pf-figure-schematic (900) — distinctly a figure, but not stretched so wide its
   thin strokes go sparse. The governed alternative to ad-hoc inline max-width on
   the SVG; retune the whole class from that one token. */
.pf-figure--schematic .pf-figure__img {
  max-width: var(--pf-figure-schematic);
}

/* ---------------------------------------------------------------------------
   DATA TIERS  (.pf-figure--table / --chart / --meter)  — v0.9.83: the data object (a table
   with its caption, a chart with its title and table twin, a meter) sits in a WHITE BOX
   (--pf-surface) on the gray stage, in the same frame a screenshot uses (.pf-figure__frame:
   --pf-ui-panel, the stage padding, the 1040 break-out), with the figcaption on the page
   below it, so a data figure and a screenshot figure share one footprint and the object
   reads as a card, the way a stat tile does in the glance band. v0.9.82 had put the three
   tiers on the stage with no box: the serif table and the chart title then sat directly on
   the gray and blended into the band around them (Louis, 2026-09-30: "get this on a white
   background box for the graph, and the title"). What goes where: the title / caption is
   INSIDE the box (it is the object's accessible name and part of the object); the figcaption
   (the takeaway) is OUTSIDE, on the page, like every other figure kind. The box is held to
   the reading measure plus its own padding: the frame's inline padding grows past the stage
   pad once the frame is wider than that, so a table's text column stays legible and the
   figcaption below, itself at the measure, lines up with the box. v0.9.84: the frame carries the
   same edge glow as every other stage. THE RULE (Louis, 2026-09-30): the glow belongs to the
   stage, so every gray panel has it (a screenshot's, a data tier's, the glance band); elevation
   belongs to a depicted screen, so only a screenshot lifts (--raised) and a data card stays flat,
   like a stat tile. Text in the box is ink / secondary on --pf-surface (the base manifest pairs,
   R4); no pink (R5).

   <figure class="pf-figure pf-figure--chart">
     <div class="pf-figure__frame">
       <div class="pf-chart">…</div>          ← or .pf-table__scroll, or .pf-meter
     </div>
     <figcaption class="pf-figure__caption">…</figcaption>
   </figure>
   --------------------------------------------------------------------------- */
.pf-figure--table,
.pf-figure--chart,
.pf-figure--meter {
  --_data-pad: var(--pf-figure-stage-pad, clamp(1.5rem, 3vw, 2.5rem));
  --_data-box-pad: var(--pf-space-3);
  width: min(var(--pf-measure-wide), calc(100vw - 2 * var(--pf-gutter)));
}
.pf-figure--table > .pf-figure__frame,
.pf-figure--chart > .pf-figure__frame,
.pf-figure--meter > .pf-figure__frame {
  position: relative;                       /* anchors the edge glow (v0.9.84) */
  box-sizing: border-box;
  width: 100%;
  padding-block: var(--_data-pad);
  padding-inline: max(var(--_data-pad), calc((100% - var(--pf-measure) - 2 * var(--_data-box-pad)) / 2));
  background: var(--pf-ui-panel);
}
/* The stage's per-edge inset glow on a data tier too (v0.9.84; same gradients as --stage, painted
   above the white box, off in dark via the tokens). pointer-events:none so the table twin's
   <summary> and the scroll regions stay reachable. */
.pf-figure--table > .pf-figure__frame::after,
.pf-figure--chart > .pf-figure__frame::after,
.pf-figure--meter > .pf-figure__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to top, var(--pf-ui-inset) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to bottom, var(--pf-ui-inset-top) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to right, var(--pf-ui-inset-side) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to left, var(--pf-ui-inset-side) 0%, transparent var(--pf-ui-inset-blur, 12px));
}
.pf-figure--table .pf-figure__frame > .pf-table__scroll,
.pf-figure--chart .pf-figure__frame > .pf-chart,
.pf-figure--meter .pf-figure__frame > .pf-meter {
  box-sizing: border-box;
  width: 100%;
  padding: var(--_data-box-pad);
  background: var(--pf-surface);
}
/* @deprecated v0.9.83: the frameless v0.9.82 form (the content element itself as the gray
   panel, no white box). Kept so an un-migrated fragment still gets a stage rather than bare
   prose width; wrap it in .pf-figure__frame instead. Target removal: v1.0.0. */
.pf-figure--table > .pf-table__scroll,
.pf-figure--chart > .pf-chart,
.pf-figure--meter > .pf-meter {
  box-sizing: border-box;
  width: 100%;
  padding-block: var(--_data-pad);
  padding-inline: max(var(--_data-pad), calc((100% - var(--pf-measure)) / 2));
  background: var(--pf-ui-panel);
}

/* Colored mat: image sits centered on a padded colored panel */
.pf-figure--mat .pf-figure__frame {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1.5rem, 4vw, 4rem);
  background: var(--pf-mat, var(--pf-surface-sunken));
}
.pf-figure--mat .pf-figure__img {
  background: transparent;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.18);
  max-width: 85%;
}

/* @deprecated: the --ui family (--ui, --ui--center, --ui--narrow, and the --ui--top/
   left/right/flush-* placements) is superseded by .pf-figure--stage for case-study
   screenshots — one uniform gray stage instead of per-shot bleed/center/narrow tuning.
   Kept for any one-off that still wants a bottom-bleed hero. Target removal: v1.0.0.

   UI mockup: a transparent (or white-chrome) screenshot on a controllable panel,
   anchored bottom-center and bleeding off the bottom edge. Flat by design — no
   outer drop shadow — so it stays on-system (R11). A light-mode-only inset glow
   at the bottom edge separates the panel from the page; it turns off in dark via
   the --pf-ui-inset token (transparent), not extra CSS. */
.pf-figure--ui .pf-figure__frame {
  position: relative;
  overflow: hidden;                              /* clips the bottom bleed      */
  aspect-ratio: var(--pf-ui-ratio, 16 / 10);
  background: var(--pf-ui-panel);
  border-radius: var(--pf-radius-none);          /* sharp/editorial; tunable    */
}
.pf-figure--ui .pf-figure__img {
  position: absolute;
  left: 50%;
  top: var(--pf-ui-headroom, 8%);                /* headroom above; runs off the bottom */
  transform: translateX(-50%);
  width: var(--pf-ui-width, 88%);
  height: auto;
  background: transparent;                       /* override the base sunken fill */
  /* Per-corner rounding — the PLACEMENT owns it, so ONE placement class carries both
     position and rounding (no need to also add a flush class). Each placement squares
     the corners on the edge the image sits against and rounds the ones floating in the
     off-white. Base = bottom-bleed → bottom two square. Corners are tokens so the
     --flush-* overrides (at the end) can compose a corner placement. */
  --pf-ui-r-tl: var(--pf-ui-img-radius, 16px);
  --pf-ui-r-tr: var(--pf-ui-img-radius, 16px);
  --pf-ui-r-br: 0;
  --pf-ui-r-bl: 0;
  border-radius: var(--pf-ui-r-tl) var(--pf-ui-r-tr) var(--pf-ui-r-br) var(--pf-ui-r-bl);
}
/* Glow painted ABOVE the image (a raw inset box-shadow on the frame would sit
   under the bleeding child img and never show). pointer-events:none so it never
   blocks the image; no z-index needed — generated content paints above children
   (R8). box-shadow is R1-exempt; the color is still tokenized for theme control. */
.pf-figure--ui .pf-figure__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Per-edge inner shadow via gradients, NOT box-shadow: a directional inset
     box-shadow's blur bleeds onto the neighbouring edges, washing out the per-edge
     difference so it reads uniform. Each gradient instead fades from that edge's
     token color to transparent over --pf-ui-inset-blur, with no bleed — so top can
     be genuinely lighter than the sides/bottom. All colors go transparent in dark. */
  background:
    linear-gradient(to bottom, var(--pf-ui-inset-top) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to top, var(--pf-ui-inset) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to right, var(--pf-ui-inset-side) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to left, var(--pf-ui-inset-side) 0%, transparent var(--pf-ui-inset-blur, 12px));
}

/* UI mockup, CENTERED: same off-white panel + edge glow (inherited from --ui),
   but the image sits fully inside with even margins instead of bleeding off the
   bottom. Overrides the base --ui absolute positioning; the panel sizes to the
   image + padding (no forced aspect), and the ::after glow still applies since
   the element also carries .pf-figure--ui. Use for figures that read better whole
   and centered rather than as a rising hero. */
.pf-figure--ui--center .pf-figure__frame {
  aspect-ratio: auto;
  padding: var(--pf-ui-center-pad, clamp(1.5rem, 5vw, 4rem));
}
.pf-figure--ui--center .pf-figure__img {
  position: static;
  inset: auto;
  transform: none;
  display: block;
  width: 100%;                                   /* fills the padded area; even padding = centered */
  height: auto;
  margin: 0;
  --pf-ui-r-br: var(--pf-ui-img-radius, 16px);   /* floats centered → all four round */
  --pf-ui-r-bl: var(--pf-ui-img-radius, 16px);
}

/* UI mockup, NARROW / COMPACT: for a tall or narrow UI shot — a sidebar pane, an
   add-in panel, a single mobile screen — that would upscale (and so soften) and
   dominate the page if stretched to the full 1040 break-out. Composes WITH
   .pf-figure--ui--center (centered, even padding, all four corners round); this only
   CAPS the figure to a compact width so the shot renders at ~native size, centered in
   the 776 reading column. This is the governed home for the "never render a raster
   past its intrinsic px" rule (R13) — a token, not an ad-hoc inline max-width. */
.pf-figure--ui--narrow {
  width: min(var(--pf-figure-ui-narrow, 560px), 100%);
  --pf-ui-center-pad: clamp(1rem, 2vw, 1.5rem);   /* tighter frame around a compact shot */
}

/* ---------------------------------------------------------------------------
   STANDARD FIGURE STAGE  (.pf-figure--stage)  — the default for a case-study
   screenshot. A uniform gray panel at the break-out width x a FIXED height
   (--pf-figure-stage-h), with padding all round, the shot object-fit-contained
   and centered so it keeps breathing room and never touches the edges. Every
   screenshot shares ONE footprint regardless of shape: a wide shot nearly fills
   the stage, a tall/portrait shot centers with gray to the sides (R13). This is
   what standardizes figure width down a case study. A light-mode-only edge glow
   separates the panel from the page (transparent in dark via --pf-ui-inset).
   Supersedes the --ui bleed/center/narrow placements for figures.
   --------------------------------------------------------------------------- */
.pf-figure--stage .pf-figure__frame {
  position: relative;
  /* Height HUGS the shot (image + padding) — no fixed tall plate, so there is no empty
     vertical band above/below a floated shot. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--pf-figure-stage-pad, clamp(1.5rem, 3vw, 2.5rem));
  overflow: hidden;
  background: var(--pf-ui-panel);
  border-radius: var(--pf-radius-none);
}
.pf-figure--stage .pf-figure__img {
  width: auto;
  height: auto;
  /* Bounded height (the shot's cap on the page); width fills the padded panel up to a px
     cap, then floats centered. min(cap, 100%) is the fix: a PERCENTAGE cap (the old
     --stage-fill) stayed that fraction of the panel at every width, so the shot shrank on
     narrow screens and the gray gutters never closed. Now a wide shot FILLS at narrow widths
     and only floats once the panel exceeds the cap; a portrait stays height-capped, floating
     with gray sides at any width. width/height:auto keeps aspect and never upscales past
     the shot's intrinsic px (R13). */
  max-height: var(--pf-figure-stage-max-h, 360px);
  max-width: min(var(--pf-figure-stage-cap, 760px), 100%);
  background: transparent;  /* override the base sunken fill */
  /* Sharp corners — the R11 editorial default. A stage screenshot floats contained on the
     gray panel; 16px rounding clipped composite / light-edged shots oddly (flagged on a
     multi-panel figure). Was var(--pf-ui-img-radius, 16px); the rounded --ui placements keep
     their own radius. */
  border-radius: var(--pf-radius-none);
}
/* Same per-edge inset glow as --ui (painted above the image; transparent in dark). */
.pf-figure--stage .pf-figure__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to top, var(--pf-ui-inset) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to bottom, var(--pf-ui-inset-top) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to right, var(--pf-ui-inset-side) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to left, var(--pf-ui-inset-side) 0%, transparent var(--pf-ui-inset-blur, 12px));
}

/* Elevation modifier (opt-in) — lifts a UI screenshot off the stage panel. Add
   .pf-figure--raised alongside --stage / --bleed / --compare; kept subtle so the frame's
   overflow:hidden barely clips it. v0.9.73: a box-shadow, NOT `filter: drop-shadow`. WebKit
   (Safari, the compat target) paints a drop-shadow filter per tile on a tall composited or
   still-decoding lazy image, so half the shot carried a shadow and half didn't until a scroll
   forced a repaint (webkit.org/b/80323 and its tiled-backing sibling; the old
   backface-visibility hint promoted the layer, which IS the tiled case). box-shadow renders in
   the normal paint path. It traces the img BOX, so every raised tier keeps the box hugging the
   picture (width/height auto + max caps; --compare below). Transparent cut-outs add --cutout. */
.pf-figure--raised .pf-figure__img {
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.22);
}
/* Cut-out (opt-in, composes with --raised) — a transparent-background composite whose panels
   should each float. drop-shadow follows the image's ALPHA, so each visible panel gets its own
   shadow (box-shadow would trace the whole, mostly transparent rectangle and do nothing for the
   panels); same technique as .pf-cover__device. The WebKit repaint artifact above is accepted
   for these few, short figures. case-study-check flags a cut-out that is raised without this. */
.pf-figure--raised.pf-figure--cutout .pf-figure__img {
  box-shadow: none;
  filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.22));
}

/* Rounded corners (opt-in) — the stage default is sharp (R11), but a single full-window
   screenshot reads better as a rounded window/card. Keep composites and cut-outs sharp (their
   own corners are transparent, so rounding them does nothing anyway). Composes with --stage.
   v0.9.88: a shot whose PNG already carries rounded corners (transparent corner pixels) MUST take
   --rounded when it is --raised: the box-shadow traces the img box, so without a matching radius
   the shadow paints a square corner around the transparent one (Louis, 2026-10-01, Contacts Fig 1
   and Accelo Fig 1). Set --pf-figure-img-radius per figure (inline, like --pf-figure-bleed-w) to
   the baked radius as a PERCENTAGE pair, "<x% of width> / <y% of height>", so the clip scales with
   the shot at every width; case-study-check measures the corners and prints the value to use. */
.pf-figure--rounded .pf-figure__img {
  border-radius: var(--pf-figure-img-radius, var(--pf-ui-img-radius, 16px));
}

/* Flat (opt-in marker) — declares a transparent cut-out is intentionally NOT elevated. On a
   composite/diagram with floating labels (e.g. a before/after with floating callout text),
   --raised's alpha-following drop-shadow would trail the text, so keep it flat. Forces no
   elevation (wins over --raised) and tells case-study-check.mjs to skip its "cut-out should be
   raised" nudge for this figure. */
.pf-figure--flat .pf-figure__img {
  filter: none;
  box-shadow: none;
}

/* Bottom-bleed (opt-in) — the shot fills the width and runs off the BOTTOM edge of the panel
   instead of floating contained, to imply the screen continues below. The frame takes a fixed
   height so a taller shot exceeds it and bleeds; the shot pins to the TOP (its top + rounded
   top corners stay visible) and overflow crops the excess at the bottom. Bottom stays square
   because it bleeds. Pair with --raised for lift (the shadow shows on the top + sides). */
.pf-figure--bleed .pf-figure__frame {
  height: var(--pf-figure-bleed-h, 460px);
  align-items: flex-start;   /* top of the shot visible; it grows DOWN past the bottom edge */
  padding-bottom: 0;         /* no bottom gutter — the shot bleeds to the very bottom edge */
}
.pf-figure--bleed .pf-figure__img {
  /* width:auto (NOT a forced fill) so a small shot renders at its intrinsic px and never
     upscales (R13); a wide shot still fills, capped at the break-out width. */
  width: auto;
  /* Cap the bleed shot's width. Defaults to the break-out cap (fills); set --pf-figure-bleed-w
     per figure (inline, like --pf-mat) to render narrower — e.g. to match a neighbouring figure
     or to keep a low-res shot from upscaling past its intrinsic px. */
  max-width: min(var(--pf-figure-bleed-w, var(--pf-figure-stage-cap, 1040px)), 100%);
  max-height: none;          /* NOT height-capped — it may exceed the frame and bleed off */
  height: auto;
  /* Corners stay sharp here (R11). Add --rounded for a rounded TOP: it rounds all four, but the
     bottom is cropped by the bleed so only the top shows. Omit it when the shot already carries
     its own corners. */
}

/* Theme-swapped figure image — provide a light AND a dark variant of the same shot
   (.pf-figure__img--light / .pf-figure__img--dark, each also carrying .pf-figure__img)
   and each shows only in its own theme. For a UI screenshot whose baked labels read in
   just one theme: pair a dark-label shot (light mode) with a light-label shot (dark mode)
   so the figure follows the page into dark instead of being pinned to a light plate.
   Gated the SAME three ways as the color tokens (system dark + explicit toggle +
   forced-light) so it tracks the manual theme switch, not only OS preference. The :root
   prefix holds specificity at/above the placement rules that set `display`, and these sit
   after them, so the correct variant wins. Composes with any --ui placement; exactly one
   variant is ever in the DOM's render + a11y tree, so give both the same alt. */
:root .pf-figure__img--dark { display: none; }                 /* light / forced-light: hide dark variant */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .pf-figure__img--light { display: none; }
  :root:not([data-theme="light"]) .pf-figure__img--dark { display: block; }
}
:root[data-theme="dark"] .pf-figure__img--light { display: none; }
:root[data-theme="dark"] .pf-figure__img--dark { display: block; }

/* Placement variants — one class sets BOTH where the image sits and which corners
   round. The base .pf-figure--ui is the bottom-bleed placement; these are the other
   sides. Top mirrors the base (bleeds off the top); left/right pin the image FLUSH
   against that side (touching the edge, margin on the other three) — a horizontal
   bleed only works for very wide shots, so flush is the deterministic choice for any
   aspect. Each squares its flush edge's corners and rounds the rest. */
.pf-figure--ui--top .pf-figure__img {
  top: auto;
  bottom: var(--pf-ui-headroom, 8%);             /* anchor low; runs off the top */
  --pf-ui-r-tl: 0;                               /* top flush */
  --pf-ui-r-tr: 0;
  --pf-ui-r-br: var(--pf-ui-img-radius, 16px);
  --pf-ui-r-bl: var(--pf-ui-img-radius, 16px);
}
.pf-figure--ui--left .pf-figure__img,
.pf-figure--ui--right .pf-figure__img {
  top: 50%;
  transform: translateY(-50%);                   /* vertically centered instead */
  width: var(--pf-ui-width, 88%);
  height: auto;
}
.pf-figure--ui--left .pf-figure__img {
  left: 0;                                        /* flush against the left edge */
  right: auto;
  --pf-ui-r-tl: 0;                               /* left flush */
  --pf-ui-r-bl: 0;
  --pf-ui-r-tr: var(--pf-ui-img-radius, 16px);
  --pf-ui-r-br: var(--pf-ui-img-radius, 16px);
}
.pf-figure--ui--right .pf-figure__img {
  left: auto;
  right: 0;                                       /* flush against the right edge */
  --pf-ui-r-tr: 0;                               /* right flush */
  --pf-ui-r-br: 0;
  --pf-ui-r-tl: var(--pf-ui-img-radius, 16px);
  --pf-ui-r-bl: var(--pf-ui-img-radius, 16px);
}
/* Flush overrides — set AFTER the placements so they win, to compose a CORNER on top
   of a single-side placement: e.g. base (bottom) + --flush-right → only top-left
   rounds. Rarely needed; the placement already rounds correctly on its own. */
.pf-figure--ui--flush-top .pf-figure__img { --pf-ui-r-tl: 0; --pf-ui-r-tr: 0; }
.pf-figure--ui--flush-bottom .pf-figure__img { --pf-ui-r-bl: 0; --pf-ui-r-br: 0; }
.pf-figure--ui--flush-left .pf-figure__img { --pf-ui-r-tl: 0; --pf-ui-r-bl: 0; }
.pf-figure--ui--flush-right .pf-figure__img { --pf-ui-r-tr: 0; --pf-ui-r-br: 0; }

/* ---------------------------------------------------------------------------
   BEFORE / AFTER  (.pf-figure--compare)  — two screenshots side by side on the
   standard stage panel, each under a short pane label, so the improvement reads
   at a glance without compositing the pair into one raster. Same footprint and
   edge glow as --stage (R13: one panel for every screenshot); the two panes share
   the stage's max height so a tall "before" and a short "after" sit level. The
   AFTER label carries a thin pink underline — a graphic accent under ink text,
   the one accessible use of pink (R5); both labels are ink (v0.9.84, was secondary on
   the before side). The panes NEVER stack (v0.9.84; they stacked at 768 before):
   stacking kills the side-by-side reading and scrolls the page through two oversized
   shots, so the pair stays level at every width and each pane's label + shot centre in
   their column.

   <figure class="pf-figure pf-figure--compare pf-figure--raised">
     <div class="pf-figure__frame">
       <div class="pf-figure__pane pf-figure__pane--before">
         <p class="pf-figure__pane-label">Before</p>
         <img class="pf-figure__img" … />
       </div>
       <div class="pf-figure__pane pf-figure__pane--after">
         <p class="pf-figure__pane-label">After</p>
         <img class="pf-figure__img" … />
       </div>
     </div>
     <figcaption class="pf-figure__caption">…</figcaption>
   </figure>
   --------------------------------------------------------------------------- */
.pf-figure--compare .pf-figure__frame {
  --_gap: var(--pf-space-3);
  position: relative;
  /* v0.9.85: a centred PAIR, not two half-width columns. With 1fr 1fr each shot sat in the
     middle of its own half, so the two drifted apart as the frame widened and the gray landed
     between them. Two max-content tracks (each grows to its shot's height-capped width, and
     both shrink evenly once the pair outgrows the frame) justified to the centre put the pair
     together in the middle with one gap; the stage padding is then what centres the pair,
     gray on the outside (Louis, 2026-09-30, on Figs 3 and 4). The frame is a size container so
     the shot's width cap (on the img below) is a definite cqw value rather than a percentage
     of a pane that is itself sized by the shot. Note for anyone measuring: a lazy shot has no
     intrinsic size until it loads, so the panes sit at label width (~70px) until then and
     then take the shot's width; measure after decode. */
  container-type: inline-size;
  display: flex;
  justify-content: center;
  gap: var(--_gap);
  align-items: flex-start;
  padding: var(--pf-figure-stage-pad, clamp(1.5rem, 3vw, 2.5rem));
  overflow: hidden;
  background: var(--pf-ui-panel);
  border-radius: var(--pf-radius-none);
}
.pf-figure__pane {
  display: flex;
  flex-direction: column;
  /* v0.9.84: centred, not flex-start. The img keeps its own width (not stretched), so its box =
     the picture; a shot narrower than its column (a tall add-in pane) used to sit at the left
     edge with a dead band to its right, so the pair read lopsided. Centred, the gray splits
     evenly either side of each shot and the two panes mirror. */
  align-items: center;
  gap: var(--pf-space-1);
  min-width: 0;
  flex: 0 0 auto;                              /* the pane hugs its shot (v0.9.85) */
}
/* Selector is (0,2,0) on purpose (v0.9.75): the label is a <p> inside the case study's
   .site-prose column, whose `.site-prose p` (0,1,1) rule sets family, size, leading and colour
   and was silently winning over the bare (0,1,0) class since the component shipped. */
.pf-figure__pane .pf-figure__pane-label {
  margin: 0;
  align-self: center;                       /* over the centred shot (v0.9.84) */
  /* v0.9.74: the display face, like the state labels over screenshots in Louis's Figma
     (condensed bold caps, 24px, -2%); was small-caps serif, which read as a caption. */
  font-family: var(--pf-font-display);
  font-weight: var(--pf-weight-display);
  font-size: var(--pf-text-figure-label);
  line-height: var(--pf-lh-display);
  text-transform: uppercase;
  letter-spacing: var(--pf-tracking-display);
  color: var(--pf-ink);                     /* v0.9.84: both labels ink (the before side was secondary and read as dimmed) */
  padding-bottom: 2px;
  border-bottom: var(--pf-border-strong) solid transparent;
}
.pf-figure__pane.pf-figure__pane--after .pf-figure__pane-label {
  border-bottom-color: var(--pf-accent);   /* the after side's only mark: a thin graphic under ink text (R5) */
}
.pf-figure--compare .pf-figure__img {
  /* width:auto + both max caps (not width:100% + object-fit): the img box then equals the
     painted picture, so a --raised box-shadow traces the shot rather than a letterboxed
     rectangle (v0.9.73). The picture keeps its aspect under both caps. v0.9.85: the width cap
     is half the frame's inner width minus half the gap, in container units, definite, so the
     pane sizes itself from the shot and the cap never depends on the pane. */
  width: auto;
  height: auto;
  max-width: calc(50cqw - var(--_gap, 1.5rem) / 2);
  max-height: var(--pf-figure-stage-max-h, 360px);
  margin: 0;
  background: transparent;                  /* override the base sunken fill */
  border-radius: var(--pf-radius-none);
}
/* Three panes (v0.9.92, opt-in) — a stepped sequence of one screen (legacy → empty → populated)
   rather than a single before / after. Same pair rules (level at every width, never stacked, each
   pane hugging its shot, the label in the display face over it); each shot's width cap is a third
   of the frame's inner width less the two gaps. The pink underline still marks only the
   `--after` pane, so put it on the end state. Author three separate exports, the same height,
   at ~2x their display width (R13), with no labels baked in. Louis, 2026-10-01: Contacts Fig 3. */
.pf-figure--compare.pf-figure--three .pf-figure__img {
  max-width: calc((100cqw - 2 * var(--_gap, 1.5rem)) / 3);
}
/* v0.9.99: the labels wrap inside their third instead of widening the pane. At a phone width each
   third is ~70px, narrower than "NEW, POPULATED" at the 24px label size, so the last pane grew past
   the screen edge and its label was clipped (publish audit, 320 and 375). Capped at the same third,
   centred, and a step smaller when the frame is narrow. */
.pf-figure--compare.pf-figure--three .pf-figure__pane .pf-figure__pane-label {
  max-inline-size: calc((100cqw - 2 * var(--_gap, 1.5rem)) / 3);
  text-align: center;
}
@container (max-width: 40rem) {
  .pf-figure--compare.pf-figure--three .pf-figure__pane .pf-figure__pane-label {
    font-size: var(--pf-text-small);
  }
}

/* Same per-edge inset glow as the stage (painted above the panes; off in dark). */
.pf-figure--compare .pf-figure__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to top, var(--pf-ui-inset) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to bottom, var(--pf-ui-inset-top) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to right, var(--pf-ui-inset-side) 0%, transparent var(--pf-ui-inset-blur, 12px)),
    linear-gradient(to left, var(--pf-ui-inset-side) 0%, transparent var(--pf-ui-inset-blur, 12px));
}
/* v0.9.84: no stacking at any width (see the header). On a phone column the gap tightens so each
   pane keeps the room; the shots get small there, but the shape of the comparison is the point
   and each alt carries the detail. */
@media (max-width: 480px) {
  .pf-figure--compare .pf-figure__frame {
    --_gap: var(--pf-space-2);
  }
}

/* Captions carry full descriptive sentences, so they read as prose (sentence
   case, normal tracking), not as a caps label. The "Fig N —" prefix is authored
   into the caption text. */
.pf-figure__caption {
  font-family: var(--pf-font-body);
  font-size: var(--pf-text-small);
  line-height: 1.5;
  color: var(--pf-ink-secondary);
  /* A caption stays at reading measure even when the figure breaks out wider, so
     caption lines never exceed a readable length under a wide image. */
  max-width: var(--pf-measure);
  /* Center the caption BLOCK under the (centered) image; text stays left-aligned
     inside it, so a caption that wraps to multiple lines keeps a shared left edge
     and stays readable (centering multi-line prose is a scanning anti-pattern). */
  margin-inline: auto;
}

/* Placeholder: a dashed frame for a figure whose asset isn't ready yet
   (case-study drafts). Not an image — the __label text says what will go
   here, so a screen reader announces the intent, not an empty/broken image.
   Height comes from aspect-ratio (no raw min-height). */
.pf-figure--placeholder .pf-figure__frame {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--pf-space-5);
  aspect-ratio: 3 / 2;
  background: var(--pf-surface-sunken);
  border: var(--pf-border-strong) dashed var(--pf-hairline-strong);
  border-radius: var(--pf-radius-none);
}

.pf-figure__label {
  margin: 0;
  max-width: 40ch;
  font-family: var(--pf-font-body);
  font-size: var(--pf-text-small);
  text-transform: uppercase;
  letter-spacing: var(--pf-tracking-caps);
  color: var(--pf-ink-secondary);
}

@media (prefers-reduced-motion: reduce) {
  .pf-figure--mat .pf-figure__img {
    box-shadow: none;
  }
}
