/* ============================================================================
   THE TYPE AND LABEL RULES FOR THIS FILE (DESIGN-BOOK §15.6, §17)

   There are two label devices and only two. Sans means eyebrow, mono means
   stamp. An eyebrow is the signpost above a headline: sans 14px (13px in a
   panel), .14em, uppercase, brand blue. A stamp is every other uppercase label
   -- data, meta, indices, figure labels: mono 11px, .13em, --muted on the page
   surface, white on artwork. Nothing here may invent a third.

   The eyebrow carries NO square dot. §3 of the book specifies one, but
   `.eyebrow .d { display: none }` in site.css has retired it on every live
   page, and the live site wins. Do not reintroduce it here.

   Display type is Afical Neue at weight 500 with letter-spacing between -.01
   and -.015em (-.02em is the floor, for the h1 only). Tighter than that is off
   brand: the h1 ran -.035em and the pull-stat -.05em before this pass.

   Geometry follows §5: outer frames and figure frames are squared, buttons and
   arrow squares are 8px, surfaces nested inside a card are 10px. No other
   radius appears in this file.

   SIZES COME FROM THE LADDER, NEVER FROM A LITERAL. The --fs-* tokens now live
   in site.css :root (ported from index.html, where they were trapped) so this
   file names roles instead of numbers. --fs-read is the one documented
   deviation: a 760px reading column sets running copy larger than --fs-body,
   which is sized for marketing copy in a 440px measure.

   BLUE IS A SIGNAL, NOT A SURFACE. On the home page the brand blue only ever
   appears as an eyebrow, a tick, an index, the uplane glyph, a 2px progress
   bar, or a button. Nothing large is filled with it. This page had four big
   blue fills -- a 124x198 engine plate, two 214x60 content blocks, four phase
   chips -- which is why it read as a different brand: a page of blue slabs next
   to a page of white cards on hairlines. The only fill left is the adoption
   chart, where blue is a measured quantity.

   ONE HAIRLINE: var(--line). #E5E7EB is the site's structural hairline, used on
   the home page for card borders, list dividers and grid dividers alike. This
   file had its own --art-hair / --fig-line / --fig-line-2 ramp at 6-12% navy,
   two to four times lighter, which is why the figures read as floating text
   rather than as the home page's framed cards.
   ========================================================================= */

.article-head {
  padding: clamp(148px, 15vw, 210px) 0 clamp(48px, 7vw, 92px);
}

/* a text-link CTA (§17): 15px/500, no rule, the arrow is the affordance */
.article-head .back {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-bottom: clamp(30px, 4vw, 52px);
  color: var(--muted);
  font-size: var(--fs-cta);
  font-weight: 500;
  transition: color .25s var(--ease-micro);
}

.article-head .back:hover {
  color: var(--ink);
}

.article-head .back span {
  font-family: inherit;
  transition: transform .2s var(--ease-micro);
}

.article-head .back:hover span {
  transform: translateX(-3px);
}

/* The topic is the signpost for the whole piece, so it takes the eyebrow; the
   read time is meta, so it takes the stamp. Both were mono blue before this,
   which spent the signal colour on a duration and left the piece without a
   signpost at all. */
.article-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 22px;
}

.article-meta > span:first-child {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--blue);
  font-family: var(--sans);
  font-size: var(--fs-eyebrow);
  font-weight: 400;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.article-meta > span + span {
  color: var(--muted);
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  letter-spacing: .13em;
  text-transform: uppercase;
}

.article-head h1 {
  max-width: 20ch;
  margin-top: 20px;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--fs-display);
  font-weight: 500;
  letter-spacing: -.015em;
  /* 1.02 put the leading below the display range the DESIGN-BOOK sanctions
     (1.05-1.12): at the 80px top of --fs-display a three-line title had its
     descenders running into the next line's caps, so the block read as one
     solid mass with no white in it. 1.08 sits mid-range. */
  line-height: 1.08;
}

/* a paragraph under an h1 is the lead (§17.4), so it takes --fs-lead's ramp */
.article-head .deck {
  max-width: 62ch;
  margin-top: 24px;
  color: var(--muted);
  font-size: var(--fs-lead);
  line-height: 1.55;
}

/* The hero band is a photographic artwork stage, the same treatment the
   Why Uplane page gives its .sit-figure squares: brand photography under a
   navy tint, with the product graphic floating on top. Pages pass their photo
   through --visual-img, the mechanism the listing thumbnails use for
   --thumb-img, so a card and its article share one image.

   It is a full-bleed stage in the site's sense (§6.5), which means its edges
   sit exactly ON the outer rails: `width: min(var(--frame-max), calc(100% -
   48px))`. It used to run the whole viewport width and crossed straight over
   both outer hairlines, so the one section on the page that should have proved
   the rail system was the one that broke it.

   Squared, always. The height is measured, not chosen: it is the tallest fully
   revealed card plus the same margin top and bottom as it has left and right,
   which is the rule §14.9 used to size `.hw-figure`. The card tops out at 467px
   at desktop widths, so 588px covers it with 56px above and below plus a small
   font-loading margin. Re-measure with tmp/stagefit.js after ANY change to the
   card's rows: an unreserved reveal is a page shift, not a taller card.

   EVERY branch reserves a height, including the narrow ones. §6.5 lets a stage
   go content-driven below 880px, which is right for the engine's multi-panel
   stage and wrong here: with `min-height: 0` the stage grew as the card revealed
   its rows and moved the whole page below it 226px through the timeline --
   exactly what §8 forbids. The narrow reserves are LARGER, not smaller, because
   the card's own text wraps more as it narrows: 467px at 1512, 500px at 390. */
.article-visual {
  position: relative;
  display: grid;
  place-items: center;
  width: min(var(--frame-max), calc(100% - 48px));
  min-height: 588px;
  margin: 0 auto;
  padding: clamp(24px, 5%, 56px);
  overflow: hidden;
  border: 0;
  border-radius: 0;
  background: #071859;
  color: #fff;
  isolation: isolate;
  overflow-anchor: none;
}

/* the card's own geometry on this stage lives with the card block below */

/* The tint is a wash, not a filter. It ran
   `rgba(7,16,84,.64) -> .26 -> rgba(5,12,70,.60)`, which is dense enough that
   the band artwork underneath read as flat navy: the home page's own stages
   carry no tint at all and let the photograph be a photograph (§16.3). What is
   left is the minimum that holds the mono stamp over a bright band, measured
   rather than guessed -- see tmp/artstamp.js. */
.article-visual::before {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg, rgba(5, 12, 70, .20), rgba(5, 12, 70, .10) 46%, rgba(4, 10, 62, .34)),
    var(--visual-img, radial-gradient(circle at 76% 28%, rgba(35, 182, 255, .75), transparent 34%))
      center / cover no-repeat,
    linear-gradient(145deg, #0b1a79 0%, #174dff 52%, #05a8e6 100%);
  content: "";
}

/* A deeper wash, for a stage whose artwork is brighter than the others' where its
   type sits.

   This is not a taste knob. tmp/artbands.mjs crops every candidate exactly as
   this stage does and reports the brightest 5% of the three bands a hero motif
   sets type in. 19.webp, which five-shifts uses, measures 7.2 / 17.1 / 17.5
   against white; the best remaining option measures 7.2 / 10.3 / 11.0, and at
   narrow widths `cover` crops to a brighter part of it still. So the same wash
   that is sufficient for the reference is not sufficient here, and the honest fix
   is more wash on this stage rather than dimmer type on it.

   Flat-ish rather than a ramp, for §16.3's reason: a gradient running across a
   line of type puts a different tone under each word. */
.article-visual--deep::before {
  background:
    linear-gradient(180deg, rgba(5, 12, 70, .54), rgba(5, 12, 70, .42) 46%, rgba(4, 10, 62, .56)),
    var(--visual-img, radial-gradient(circle at 76% 28%, rgba(35, 182, 255, .75), transparent 34%))
      center / cover no-repeat,
    linear-gradient(145deg, #0b1a79 0%, #174dff 52%, #05a8e6 100%);
}

@media (max-width: 880px) {
  .article-visual {
    min-height: 600px;
    padding: clamp(28px, 7vw, 44px) 20px 56px;
  }
}

@media (max-width: 560px) {
  .article-visual {
    min-height: 600px;
  }
}

@media (max-width: 440px) {
  .article-visual {
    min-height: 672px;
  }
}

/* The reading column is a three-track grid: text lives in the centre
   min(760px) track, and structural graphics (figures, pull-stats, summaries,
   sources, next) span all three tracks and bleed a further 24px so their
   rules and box edges land exactly on the inner rails instead of ending in
   the air. Text inside a bleed block pads the 24px back, so nothing moves. */
.article-body {
  /* The site's structural hairline, the same one the home page draws every card
     border, list divider and stat divider with. It was a 6% navy tint, which is
     roughly a third of this weight: at that value a figure frame disappeared and
     the summary grid read as four floating paragraphs. */
  --art-hair: var(--line);
  /* the home page's `.ck` path, at its stroke weight and caps, as a mask */
  --tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E");
  display: grid;
  grid-template-columns: minmax(0, 1fr) min(760px, 100%) minmax(0, 1fr);
  width: 100%;
  margin: 0 auto;
  padding: clamp(64px, 8vw, 120px) 0;
}

.article-body > * {
  grid-column: 2;
  min-width: 0;
}

.article-body > .article-figure,
.article-body > .pull-stat,
.article-body > .article-summary,
.article-body > .article-quote,
.article-body > .article-sources,
.article-body > .article-next {
  grid-column: 1 / -1;
  width: auto;
  margin-inline: -24px;
}

@media (max-width: 1100px) {
  /* the rails are hidden below 1100px, so there is nothing to bleed to */
  .article-body > .article-figure,
  .article-body > .pull-stat,
  .article-body > .article-summary,
  .article-body > .article-sources,
  .article-body > .article-next {
    margin-inline: 0;
  }
}

/* The next-article block is bracketed by a rule top and bottom, and with the
   outdent zeroed those two rules stopped 24px short of each side -- reviewed as
   "make the bottom line broader because it cuts off the entire segment, it can
   touch the left and right edge of the display". The box takes the screen and
   pads its own content back onto the rail, so the rules run edge to edge while
   the thumbnail and the copy stay where every other line on the page sits. */
@media (max-width: 640px) {
  .article-body > .article-next {
    margin-inline: -24px;
    padding-left: 24px;
    padding-right: 24px;
  }
}

/* Opening lede: the first paragraph speaks in the display face, the same
   move the welcome article makes, before the column settles into body copy.
   Sized on --fs-quote (§17), the ladder's role for a display paragraph; it was
   31px at -.018em, which is between two ladder rungs and tighter than the
   -.015em display floor. */
.article-body .article-lede {
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--fs-quote);
  font-weight: 500;
  letter-spacing: -.015em;
  line-height: 1.3;
}

.article-body > .article-lede + p {
  margin-top: 24px;
}

/* Section kicker: the signpost above each h2, so it is the EYEBROW device --
   sans, 14px, .14em, brand blue (§15.6). It was mono 11px, which is the stamp,
   and the page therefore had five stamps where it wanted five eyebrows and no
   eyebrow anywhere. The index inside it stays in the eyebrow's own face, the
   way index.html's .solution-kicker sets its `01` / `02`.

   Each numbered section opens on a hairline that lands on the inner rails, so
   the sections read as the page's top-level structure and the rule belongs to
   the rail system rather than floating at an arbitrary margin (§4.2 rule 4).
   Reaching the rails is why the kicker is a BLEED BLOCK like `.pull-stat` and
   `.article-next`: it spans all three tracks, bleeds the same 24px, and re-
   declares the three-track grid so its label drops back onto the text column.
   Drawn any other way the rule cannot find the rails -- a `::before` at
   `left: -24px` inside the 760px text track measured 352px against an inner
   rail at 188px, which is aligned with nothing on the page. */
.article-body > .kicker {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) min(760px, 100%) minmax(0, 1fr);
  width: auto;
  margin-top: clamp(62px, 8vw, 96px);
  margin-inline: -24px;
  padding-top: clamp(30px, 3.4vw, 44px);
  border-top: 1px solid var(--art-hair);
}

/* A block that already closes on a full-width rule does not need a second one.
   `.pull-stat` and `.article-summary` both end on a hairline at the inner rails,
   so a kicker straight after them drew a parallel rule 180px below the first
   (84px of the stat's bottom margin plus 96px of the kicker's top), which is a
   void bounded by two identical lines. The preceding rule becomes the section
   boundary and the gap closes to one step. */
.article-body > .pull-stat + .kicker,
.article-body > .article-summary + .kicker {
  margin-top: clamp(34px, 4vw, 52px);
  padding-top: 0;
  border-top: 0;
}

/* the label is a real element, not a bare text node: as a text node it becomes
   an anonymous grid item and lands in the left gutter track */
.article-body .kicker .kt {
  grid-column: 2;
  color: var(--blue);
  font-family: var(--sans);
  font-size: var(--fs-eyebrow);
  font-weight: 400;
  letter-spacing: .14em;
  text-transform: uppercase;
}

@media (max-width: 1100px) {
  /* the rails are hidden here, so there is nothing to bleed to */
  .article-body > .kicker {
    margin-inline: 0;
  }
}

/* The square dot was retired site-wide: `.eyebrow .d { display: none }` in
   site.css hides it on every other page, so the articles were the only place
   still drawing it. Kept in the markup so one rule governs it again. */
.article-body .kicker .d,
.afig-eyebrow .d {
  display: none;
}

.article-body .kicker + h2 {
  margin-top: 16px;
}

.article-body > p,
.article-body > ul,
.article-body > ol {
  color: var(--text);
  font-size: var(--fs-read);
  line-height: 1.78;
}

/* Blocksatz for the running copy. Justification only behaves on a measure this
   wide (760px at --fs-read is ~80 characters) and only with hyphenation on --
   without it English sets rivers and word gaps. The document is lang="en", so
   `hyphens: auto` gets the right dictionary.

   Scope is deliberately the body paragraphs alone: the serif .article-lede sets
   at --fs-quote/1.3, where justifying opens visible holes, and lists/captions
   are too short a measure to justify cleanly. */
.article-body > p:not(.article-lede) {
  text-align: justify;
  -webkit-hyphens: auto;
  hyphens: auto;
}

.article-body > p + p,
.article-body > p + ul,
.article-body > ul + p,
.article-body > ol + p {
  margin-top: 20px;
}

.article-body h2 {
  margin: clamp(62px, 8vw, 96px) 0 20px;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--fs-h2);
  font-weight: 500;
  /* -.01em is the base display tracking (site.css `h1, h2, h3`). This ran
     -.025em, two and a half times tighter than any headline on the home page. */
  letter-spacing: -.01em;
  line-height: 1.02;
}

.article-body h3 {
  margin: 38px 0 12px;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.01em;
  line-height: 1.06;
}

.article-body ul {
  display: grid;
  gap: 12px;
  margin-top: 20px;
  padding: 0;
  list-style: none;
}

.article-body li {
  position: relative;
  padding-left: 30px;
}

/* The brand's list mark is a BLUE TICK, not a square: index.html leads every
   USP row with `.ck`, a 24-unit path at stroke-width 2.4 with round caps, and
   the square is only ever the (retired) eyebrow dot. Drawn as a mask so the
   glyph is the same path the home page ships, at the same weight and caps,
   while staying a ::before and needing no markup. */
.article-body li::before {
  position: absolute;
  top: .38em;
  left: 0;
  width: 17px;
  height: 17px;
  background: var(--blue);
  -webkit-mask: var(--tick) center / contain no-repeat;
  mask: var(--tick) center / contain no-repeat;
  content: "";
}

/* the underline was #1436FF at 38%, a bright blue that is not in the palette */
.article-body a:not(.btn) {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: rgba(26, 68, 255, .38);
  text-underline-offset: .16em;
  transition: text-decoration-color .25s var(--ease-micro);
}

.article-body a:not(.btn):hover {
  text-decoration-color: var(--blue);
}

/* ---- A list of claims takes the home page's USP row anatomy ----
   index.html #solutions sets its three USPs as hairline-separated rows led by
   a blue tick. A list that asserts things (the three principles here) is the
   same object, so it uses the same device instead of the square bullet, which
   stays the default for ordinary lists. */
.article-body ul.checks {
  gap: 0;
  margin-top: 28px;
  border-top: 1px solid var(--art-hair);
}

.article-body ul.checks li {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 58px;
  padding: 12px 0;
  border-bottom: 1px solid var(--art-hair);
}

.article-body ul.checks li::before {
  display: none;
}

.article-body ul.checks .ck {
  width: 17px;
  height: 17px;
  flex: none;
  color: var(--blue);
}

.article-body sup {
  margin-left: 2px;
  color: var(--blue);
  font-family: var(--sans);
  font-size: .65em;
  font-weight: 600;
}

/* The stat block: rules run rail to rail while the numeral and its sentence
   stay on the text column, so the page gains a full-width beat without the
   copy ever leaving its measure. The numeral and text sit side by side from
   720px up, which stops a two-line sentence reading as a caption. */
.pull-stat {
  display: grid;
  grid-template-columns: minmax(0, 1fr) min(760px, 100%) minmax(0, 1fr);
  margin: clamp(54px, 7vw, 84px) 0;
  padding: clamp(30px, 4vw, 48px) 0;
  border-block: 1px solid var(--art-hair);
}

.pull-stat strong,
.pull-stat span {
  grid-column: 2;
}

/* --fs-display-word (§17.1) is the ladder's role for a big stat numeral, and
   -.015em is the display floor. This was clamp(48px, 8vw, 92px) at -.05em: a
   size no other numeral on the site uses, set more than three times tighter
   than any other display line, which is why the digits touched. */
.pull-stat strong {
  display: block;
  color: var(--blue);
  font-family: var(--serif);
  font-size: var(--fs-display-word);
  font-weight: 500;
  letter-spacing: -.015em;
  line-height: 1;
}

.pull-stat span {
  display: block;
  max-width: 46ch;
  margin-top: 18px;
  color: var(--ink);
  font-size: var(--fs-lead);
  line-height: 1.5;
}

/* Side by side from 720px up. The two tracks total 760px, the same measure as
   the text column, so the pair stays centred on the reading axis while the
   rules above and below still run out to the rails. */
@media (min-width: 720px) {
  .pull-stat {
    grid-template-columns:
      minmax(0, 1fr)
      minmax(0, 240px)
      minmax(0, 520px)
      minmax(0, 1fr);
  }

  .pull-stat strong {
    grid-area: 1 / 2;
    align-self: start;
  }

  .pull-stat span {
    grid-area: 1 / 3;
    max-width: none;
    margin-top: 6px;
    align-self: start;
  }
}

/* ============================================================================
   Article figure kit
   Diagrams are built in markup, not placed as images, so they inherit the
   type ramp, respond at every width, and stay editable. They sit in the
   reading column and take the same vertical rhythm as .pull-stat.
   Geometry follows DESIGN-BOOK §5: the figure frame is squared (radius 0) and
   every surface nested inside it takes the sub-row radius, 10px. The kit used
   to mix 2 / 3 / 5 / 6 / 8px, none of which is a radius the site owns.
   ========================================================================= */
.article-figure {
  /* one hairline, the site's own: internal dividers and connector wires both
     draw at --line, the weight index.html gives every card border and every
     list divider. The old ramp was 6% and 12% navy, so a divider inside a
     figure was lighter than the text it separated and the connector wires
     antialiased to nothing. --fig-tint survives for the sibling articles. */
  --fig-line: var(--line);
  --fig-line-2: var(--line);
  --fig-tint: rgba(26, 68, 255, .07);
  /* A diagram draws itself through these, never through the page tokens
     directly, so the same markup can sit on white or on warm paper: the stage
     variant below re-points all six at the agent-log card's tones (§7.1) and
     every rule inside follows. Brand blue is deliberately NOT among them -- it
     is the one colour that is correct on both surfaces. */
  --fig-surface: var(--card);
  --fig-tile: var(--cream);
  --fig-tile-2: var(--cream-2);
  --fig-ink: var(--ink);
  --fig-text: var(--text);
  --fig-muted: var(--muted);
  width: 100%;
  margin: clamp(54px, 7vw, 84px) 0;
}

.article-figure img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--fig-line);
  background: #fff;
}

/* A figure is the same object as index.html's `#solutions .solution-card`, so
   it takes that card's anatomy exactly: white surface, 1px --line frame, radius
   0, and the one card lift the site owns (--shadow-card). Without the lift a
   white card on a white page is only a hairline, which is why these diagrams
   read as loose text while the home page's cards read as objects. */
.afig {
  border: 1px solid var(--line);
  border-radius: 0;
  background: var(--card);
  box-shadow: var(--shadow-card);
}

/* ============================================================================
   .afig--stage : a diagram presented the way the site presents every other
   graphic (DESIGN-BOOK §6.5, §7)

   why-us `#situations`, `#how` on the home page and `#implementation` at the
   foot of the enterprise page all do the same thing: a SQUARED band-art panel
   with exactly ONE warm-paper card floating on it, a mono stamp on the artwork,
   and the prose beside or beneath it. The article's figures were the only
   graphics on the site drawn on plain white with no artwork anywhere, which is
   why they read as diagrams pasted into a document rather than as the site's
   own product graphics.

   The card is `.alog`'s recipe verbatim -- #F6F4F3, radius 16px, the same two
   shadows, #2F2C2B ink, a white-glass hairline -- because § 7 says every product
   graphic on the site is one of these. What sits inside it is this page's own
   drawing, which is exactly what why-us does with `.sit-fov` inside an
   `.al-act.blk`: a page-specific block in the shared card.

   `min-height` is reserved so the artwork loading cannot shift the page (§11.5).
   ========================================================================= */
.afig--stage {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 520px;
  padding:
    clamp(58px, 6vw, 84px)
    clamp(24px, 3.5vw, 52px)
    clamp(30px, 4vw, 56px);
  overflow: hidden;
  border: 0;
  border-radius: 0;
  /* A top-down wash at the minimum strength that holds a white mono stamp over
     a bright band (§ 14.9 hit this on the hero; § 16.3 is why it stops at 22%
     and lets the rest of the photograph be a photograph). The fallback colour
     matches the stage while the artwork loads. */
  background:
    linear-gradient(180deg, rgba(4, 10, 62, .30) 0, rgba(4, 10, 62, 0) 22%),
    var(--fig-img, none) center / cover no-repeat,
    #0B1A5E;
  box-shadow: none;
  isolation: isolate;
  overflow-anchor: none;
}

/* The figure's signpost, on artwork: the stamp device at
   rgba(255,255,255,.72), which is § 15.6's value for a label over a photograph.
   It replaces the white `.afig-head` band -- a stage carries its label ON the
   artwork (`.stage-stamp`, `.sit-num`), it does not sit under a header bar.

   It is a SIBLING of the stage in the markup rather than a child of it, which
   changes nothing here -- the stage starts at the figure's own top edge, so the
   same offsets land on the same pixel -- and lets the phone set it as a blue
   eyebrow above the box instead. See the round-two phone block at the foot of
   this file. */
.article-figure { position: relative; }

.article-figure > .afig-stamp {
  position: absolute;
  top: clamp(24px, 2.8vw, 34px);
  left: clamp(26px, 3.5vw, 40px);
  z-index: 1;
  color: rgba(255, 255, 255, .72);
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  letter-spacing: .13em;
  text-transform: uppercase;
}

/* THE PHONE-TRACK WRAPPERS.
   Three graphics carry a header, a subject line or a footer alongside the parts
   that swipe on a phone, so the parts need a box of their own to be the track.
   `display: contents` makes that box invisible to the layout above the phone
   breakpoint: the era columns stay in `.eras-graphic`'s subgrid, the two
   records stay in `.afig-versus`'s two-column grid, and the two brief notes
   stay direct rows of `.afig-control`. Below it, `[data-m-swipe]` in
   assets/site.css turns each one into a flex scroller. */
.eras-cols,
.vs-cols,
.ctl-notes { display: contents; }

/* THE WIDE-DRAWING SCROLLER.
   Four drawings are laid out on a 480- or 660-unit canvas with 10px labels,
   which is a 1:1 rendering at 480-660px wide. Fit to a 246px phone card they
   render at 0.42-0.57, so those labels resolved to 3.9-5.7px on the device the
   article is mostly read on -- the axis of the customer-time chart, the two
   ends of the gate, the lane tags. The drawings are correct; the width is what
   is missing. Below the breakpoint this box hands them their own width back
   and scrolls, so every label renders at the size it was drawn at. Above it
   there is width to spare and the box is not in the layout at all. */
.afig-scroll { display: contents; }

.afig--stage .afig-card {
  position: relative;
  width: min(94%, var(--fig-card-w, 780px));
  padding: clamp(22px, 2.4vw, 28px) clamp(22px, 2.6vw, 30px) clamp(22px, 2.4vw, 28px);
  border: 1px solid rgba(255, 255, 255, .42);
  border-radius: 16px;
  background: #F6F4F3;
  box-shadow: 0 2px 6px rgba(6, 10, 40, .08), 0 36px 90px rgba(6, 10, 40, .30);
  color: #2F2C2B;
  text-align: left;

  /* The card's tones, from `.sit .alog` and § 7.1. Two hairline weights, not
     one: --fig-line divides rows (.10-.12 is the card's own `--card-hair`), and
     --fig-line-2 draws the connector wires, which carry the argument and
     antialias away at the row weight. --fig-tile is two steps off the card, not
     one -- at #F1EEEC a tile on #F6F4F3 was invisible. */
  --fig-line: rgba(47, 44, 43, .12);
  --fig-line-2: rgba(47, 44, 43, .24);
  --fig-surface: #FCFAF9;
  --fig-tile: #EAE4E0;
  --fig-tile-2: #E4DEDA;
  --fig-ink: #2F2C2B;
  --fig-text: #2F2C2B;
  --fig-muted: rgba(47, 44, 43, .58);
  /* --fig-tint was the one token this block forgot to re-point. The white-page
     value is 7% blue, and 7% blue over #F6F4F3 warm paper resolves lilac. At 4%
     over the card's own tone the highlighted row reads as blue-tinted paper,
     and the 2px blue edge beside it does the rest of the work. */
  --fig-tint: rgba(26, 68, 255, .04);
}

/* The figure cards carried a head: the Uplane glyph, one bold line, and a
   hairline under it. It went, after review. Every figure already names itself
   twice -- a mono stamp on the stage above the card, and a figcaption under it
   carrying the so-what -- so the head was a third label on the same object, and
   on the cards whose body has its own column header (the candidate-action
   matrix, the persona/context pair) it produced two header bands and two
   hairlines 30px apart. The grammar is now stamp, drawing, caption: what this
   is, the evidence, what it means. `.afig-glyph`, `.afig-card-title` and the
   `#upGlyph` symbol each article carried went with it, unreferenced. */

/* the card supplies the inset now */
.afig--stage .afig-body { padding: 0; }

/* The one thing the removed head was carrying that nothing replaced: WHOSE data
   this is and WHEN. A stamp names the device ("Customer time") and a caption
   says what it means, but neither says that the marks belong to one account at
   one operator across three named months, so every label in the drawing had to
   be read as an abstraction. This is not the old head returning: muted rather
   than bold, no glyph, no hairline, and it is a sentence about the subject
   rather than a title for the card. */
.afig-subject {
  margin: 0 0 clamp(18px, 2vw, 24px);
  /* full card width, not a 68ch measure: the line names the subject of the
     drawing under it, so it is part of that drawing's box rather than body
     copy, and a short measure left it hanging over an empty right half. */
  color: var(--fig-muted);
  font-size: 12.5px;
  line-height: 1.5;
}

/* .afig-versus is a two-column grid, so a subject dropped into it becomes the
   first CELL: the persona took column two and the decision context fell to the
   next row, which broke the one thing that figure is (two records side by
   side). It spans instead, in any grid-bodied figure. */
.afig-versus > .afig-subject { grid-column: 1 / -1; }

/* ---- the article's closing ask -------------------------------------------
   It used to be prose: a bold question, a sentence, a "->" glyph, and a text
   link, all inside the justified body column. Nothing else on this site asks
   for anything that way. This is the same request as a block: a top hairline
   to separate it from the argument above, the sentence at label weight, and
   the site's own .btn.btn-blue, so the article's ask and the header's ask are
   the same object. */
.article-cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 18px 32px;
  margin-top: clamp(32px, 4vw, 44px);
  padding-top: clamp(24px, 3vw, 32px);
  border-top: 1px solid var(--fig-line, rgba(47, 44, 43, .12));
}

.article-cta p {
  max-width: 46ch;
  margin: 0;
  text-align: left;
  font-size: var(--fs-label);
  line-height: 1.5;
  color: var(--muted);
}

.article-cta p strong {
  display: block;
  font-weight: 500;
  color: var(--ink);
}

.article-cta .btn {
  flex: none;
  gap: 8px;
  padding: 13px 22px;
  font-size: var(--fs-label);
}

/* the arrow travels on hover, the movement every other CTA on the site makes */
.article-cta .btn svg {
  transition: transform .4s var(--ease-micro, cubic-bezier(.22, 1, .36, 1));
}

.article-cta .btn:hover svg { transform: translateX(3px); }

@media (max-width: 620px) {
  .article-cta { align-items: flex-start; flex-direction: column; }
  .article-cta .btn { width: 100%; justify-content: center; }
}

@media (max-width: 700px) {
  .afig--stage {
    min-height: 0;
    padding: clamp(52px, 12vw, 68px) 16px clamp(22px, 5vw, 34px);
  }

  .afig--stage .afig-card { width: 100%; }
}

/* `.article-body li` and `.article-body ul` are descendant selectors written
   for prose lists, and they were reaching into the diagrams: the blue square
   bullet and its 24px inset, plus a 20px top margin, a 12px row gap and the
   1.78 prose line-height. The margin was the visible bug -- it pushed each
   figure list 20px below the connector column stretched beside it, so every
   wire left its row 4 to 16px high. Reset here (one class + one type, declared
   after the prose rules, so it wins) and let the components below re-declare
   what they need with two classes.

   Deliberately does NOT reset `gap`: a one-class component rule cannot
   out-specify `.article-body ul`, so zeroing it here would silently flatten any
   figure list that had asked for its own row gap. A component that wants a gap
   inside a figure has to say so at two classes, the way `.afig-orch
   .orch-signals` does. */
.afig li { padding-left: 0; }
.afig li::before { content: none; }

.afig ul,
.afig ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* the card inset index.html gives `.solution-copy`: it scales with the card
   instead of holding a fixed 24px against a 1136px frame */
.afig-head {
  padding: clamp(24px, 2.6vw, 34px) clamp(26px, 3vw, 38px) clamp(22px, 2.4vw, 30px);
  border-bottom: 1px solid var(--fig-line);
}

/* an in-panel signpost, so it is the eyebrow at 13px (§15.6), not a stamp */
.afig-eyebrow {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--blue);
  font-family: var(--sans);
  font-size: var(--fs-eyebrow);
  font-weight: 400;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* a card title is the display face at h3 scale (§15.6, §16.4), not a label */
.afig-title {
  margin-top: 11px;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.012em;
  line-height: 1.12;
}

.afig-body { padding: clamp(26px, 3vw, 38px); }

/* The diagram card spans the rails; its caption returns to the text column.
   Both are centred on the same axis, so a 760px centred caption aligns
   exactly with the body copy above it. */
.article-figure figcaption {
  width: min(760px, 100%);
  margin: 18px auto 0;
  color: var(--muted);
  font-size: var(--fs-meta);
  line-height: 1.55;
}

/* ---- A. orchestration: signals -> engine -> decisions ----
   Five tracks: signals, a wire column, the engine, a wire column, decisions.
   The two wire columns are grids with the same number of equal (1fr) rows as
   the list beside them, so every hairline leaves a row's exact centre and the
   trunk line leaves the column's exact middle, which is the engine's centre.
   No pixel heights are hard-coded anywhere, so a wrapping label moves the row
   and its wire together. Replaces two 26px stubs that started at a random
   pill edge and ended in empty air (DESIGN-BOOK §8: endpoints must land on
   something). The whole diagram is centred rather than stretched: at 1136px a
   stretched version left the decision column two thirds empty. */
.afig-orch {
  display: grid;
  grid-template-columns: minmax(0, 300px) 44px 152px 44px minmax(0, 320px);
  justify-content: center;
  align-items: stretch;
  gap: 0;
}

/* two classes throughout, so these beat the `.afig ul` reset above */
/* The signal list is index.html's USP row, not a stack of chips: hairline-
   separated rows on the page surface, one line icon, --fs-body in --text,
   radius 0. It was five blue-tinted rounded pills, and the site owns no tinted
   pill -- a chip on the home page is either squared and borderless
   (`.solution-chips span`) or a 999px tag inside a product graphic. A tinted
   rounded box in content is neither. */
/* The head rule is a pseudo-element, not a border: a border on a grid shrinks
   its content box by 1px, so the five rows became (h-1)/5 while the connector
   column beside it kept h/5 and every wire left its row about 0.7px high. */
.afig-orch .orch-signals {
  position: relative;
  display: grid;
  grid-template-rows: repeat(5, minmax(46px, 1fr));
  gap: 0;
}

.afig-orch .orch-signals::before {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--fig-line);
  content: "";
}

.orch-signals li {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 0 10px 0 0;
  border-bottom: 1px solid var(--fig-line);
  color: var(--fig-text);
  /* a row inside a card is 14px -- `.al-step`'s size, not --fs-body's 15px */
  font-size: 14px;
  line-height: 1.4;
}

/* The icon spec, from index.html's `.al-sub` rows: 24-unit box, no fill, round
   caps and joins, stroke 1.8, muted. Blue is reserved for the tick, the index
   and the glyph, so a row icon is never blue -- and the five rows carry five
   different icons, because the home page never repeats one mark down a list. */
.orch-signals svg {
  flex: none;
  width: 18px;
  height: 18px;
  color: var(--fig-muted);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---- the wires ----
   in:  one hairline per signal row from the pill edge to the trunk, the trunk
        spanning first row centre to last row centre, then one line to the engine
   out: the mirror image
   The trunk is assembled from per-row segments so it needs no pixel offsets:
   every row draws a full-height segment plus the gap beneath it, and the first
   and last rows start/stop at their own centre. */
.orch-wire {
  position: relative;
  display: grid;
  gap: 7px;
}

.orch-wire.is-in { grid-template-rows: repeat(5, 1fr); gap: 0; }
.orch-wire.is-out { grid-template-rows: repeat(3, 1fr); gap: 0; }

.orch-wire i { position: relative; }

/* the branch: pill edge -> trunk (in), trunk -> decision (out) */
.orch-wire i::before {
  position: absolute;
  top: 50%;
  height: 1px;
  background: var(--fig-line-2);
  content: "";
}

.orch-wire.is-in i::before { left: 0; right: 50%; }
.orch-wire.is-out i::before { left: 50%; right: 0; }

/* the trunk */
.orch-wire i::after {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  background: var(--fig-line-2);
  content: "";
}

.orch-wire i:first-child::after { top: 50%; }
.orch-wire i:last-child::after { bottom: 50%; }

/* the trunk line into or out of the engine, on the column's own centre */
.orch-wire::after {
  position: absolute;
  top: 50%;
  height: 1px;
  background: var(--fig-line-2);
  content: "";
}

.orch-wire.is-in::after { left: 50%; right: 0; }
.orch-wire.is-out::after { left: 0; right: 50%; }

/* The engine is a CARD, not a blue plate. index.html never fills a surface
   this size with brand blue: a focal object there is a white card on a --line
   hairline carrying the card lift, and the blue arrives as the uplane glyph
   inside it -- which is precisely the agent-log card head (blue mark, ink
   title). A 124x198 solid --blue block was the largest single brand break on
   the page. Squared, because a figure's internals follow the figure. */
/* Centred, not stretched. Stretching made the card as tall as the tallest
   column (287px at desktop once the rows grew), which read as an empty
   vertical panel rather than a card. Its centre is still the grid row's centre,
   which is where both trunk lines arrive, so nothing moves. */
.orch-engine {
  position: relative;
  align-self: center;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: 150px;
  padding: 20px 12px;
  border: 1px solid var(--fig-line);
  /* 10px is § 5's radius for a surface nested inside a card, and `.al-sub`'s
     value. No drop shadow: this sits ON the card, so a second lift would be
     card-on-card. On the white variant --fig-surface is still #FFFFFF. */
  border-radius: 10px;
  background: var(--fig-surface);
  color: var(--fig-ink);
  text-align: center;
}

.orch-engine svg { width: 26px; height: 26px; color: var(--blue); }

/* ---- in-figure keys are the inline bold label, not a stamp ----
   The uppercase device belongs to a signpost above a headline and to a stamp
   on data or artwork, one per block. Inside a diagram it multiplied: this
   figure alone shouted ORCHESTRATION, DECISION ENGINE, WHAT, WHEN, WHERE and
   THE ORCHESTRATION LAYER in one view, six labels competing at the same pitch
   with no hierarchy between them. The eyebrow and the caption stamp stay
   uppercase because they label the whole figure, and so do the card kickers in
   the summary grid, which are kickers above card titles. Every key *inside* a
   drawing takes the inline bold label instead (§17.2's --fs-label): sans, 500,
   sentence case, ink, white on the blue plate. It is not a third device -- the
   ink and the absence of tracking are what keep it off the eyebrow, which is
   always blue, uppercase and tracked .14em. Applies to the decision keys and to
   the two ends of the adoption scale. */
.orch-engine span {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.005em;
  line-height: 1.25;
}

/* the same object as the signal list, so the same anatomy: a pseudo-element
   head rule (a border would shrink the grid and break the wires) and one rule
   under every row */
.afig-orch .orch-out {
  position: relative;
  display: grid;
  grid-template-rows: repeat(3, 1fr);
  gap: 0;
}

.afig-orch .orch-out::before {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--fig-line);
  content: "";
}

.orch-out li {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 12px 0 12px 16px;
}

.orch-out li { border-bottom: 1px solid var(--fig-line); }

.orch-out b {
  display: block;
  margin-bottom: 4px;
  color: var(--fig-ink);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.005em;
}

/* 14px/500 ink over 12.5px/400 muted: `.al-step` over `.al-step .mt`, the
   card's own label/meta pair. Weight and colour carry the hierarchy. */
.orch-out span {
  display: block;
  color: var(--fig-muted);
  font-size: 12.5px;
  line-height: 1.45;
}

/* Stacked: the wires would have to turn a corner, so they go and the engine
   grows its own short stubs top and bottom, which do land on a block. */
@media (max-width: 820px) {
  .afig-orch { grid-template-columns: 1fr; gap: 18px; }
  .orch-wire { display: none; }
  .afig-orch .orch-signals { grid-template-rows: repeat(5, 46px); }
  .orch-engine { min-height: 0; flex-direction: row; gap: 12px; }
  .orch-engine::before,
  .orch-engine::after {
    position: absolute;
    left: 50%;
    width: 1px;
    height: 18px;
    background: var(--fig-line-2);
    content: "";
  }
  .orch-engine::before { bottom: 100%; }
  .orch-engine::after { top: 100%; }
  .afig-orch .orch-out { grid-template-rows: auto; }
  .orch-out li { padding-left: 0; }
}

/* ---- B. atomic components recombined into dynamic content ----
   The drawing is capped instead of fluid. At `width: 100%` in this column the
   480-unit viewBox scaled 1.85x, which turned 4px corners into 7px, 44px tiles
   into 81px, and made this figure 759px tall against 503px and 530px for the
   other two. Capped at 520px it renders near 1:1, the radii read as drawn, and
   the figure sits in the same height band as its siblings. */
.afig-atomic {
  display: grid;
  grid-template-columns: minmax(0, 280px) minmax(0, 520px);
  justify-content: center;
  gap: 56px;
  align-items: center;
}

/* On a card the two tracks no longer have 1088px to share, so the fixed pair
   overflowed and the drawing shrank to 323px while the key kept its 280px.
   The key takes what it needs and the drawing takes the rest. */
.afig--stage .afig-atomic {
  grid-template-columns: minmax(0, 224px) minmax(0, 1fr);
  justify-content: stretch;
  gap: 30px;
}

.atomic-key { display: flex; flex-direction: column; gap: 22px; }

.atomic-key h4 {
  color: var(--fig-ink);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.008em;
}

/* The legend swatch IS the shape it stands for, at 12px: a cream part tile, and
   a white built tile under its blue signal bar. It used to be a 22%-blue square
   beside a solid blue square, the only place on the site where brand blue was a
   fill at two strengths. */
.atomic-key .sw {
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-right: 9px;
  border: 1px solid var(--fig-line);
  border-radius: 0;
  background: var(--fig-tile);
  vertical-align: baseline;
}

/* The swatch carried a 3px blue bar across its head, mirroring the signal bar
   the SVG draws on a built asset. At 12px that bar is a quarter of the whole
   square, so it read as a striped chip rather than as the white tile it stands
   for. The distinction the legend actually has to carry is cream part vs white
   output, and the fill alone carries it; the blue stays where it is legible,
   on the 56x40 blocks in the drawing. */
.atomic-key .sw-b {
  background: var(--fig-surface);
}

.atomic-key p {
  margin-top: 6px;
  color: var(--fig-muted);
  font-size: 12.5px;
  line-height: 1.5;
}

.atomic-svg {
  display: block;
  width: 100%;
  height: auto;
}

/* Everything here is a white tile on a --line hairline, the site's card
   anatomy at diagram scale. The two built assets were 214x60 slabs of solid
   --blue, the second-largest blue fill on the page; they are now the same white
   tile carrying a 3px blue bar, which is how the site marks a result -- the
   `.al-prog` progress bar is the same 2px of blue on a neutral track. */
/* A part is raw material, so it takes --cream, the palette's neutral light
   surface -- the same tone index.html gives a tile inside a product graphic.
   Pure white on a white card was only a hairline outline and read as an empty
   placeholder box. */
.atomic-svg .at-part {
  fill: var(--fig-tile);
  stroke: var(--fig-line);
}

.atomic-svg .at-wire {
  fill: none;
  stroke: var(--fig-line-2);
}

/* A built asset is finished, so it is the white card surface, and the ONE blue
   element in the drawing is the 3-unit signal bar across its head -- the same
   move `.al-prog` makes with 2px of blue on a neutral track. Parts read cream,
   output reads white-plus-signal, and the figure spends brand blue once. */
.atomic-svg .at-built {
  fill: var(--fig-surface);
  stroke: var(--fig-line);
}

.atomic-svg .at-cap { fill: var(--blue); }

/* the seams inside a built asset: one per component feeding it, so the block
   reads as assembled rather than as one surface */
.atomic-svg .at-seam { stroke: var(--fig-line); }

@media (max-width: 640px) {
  /* `.afig--stage .afig-atomic` above is 0-2-0 and this rule was 0-1-0, so the
     stage kept its `minmax(0,224px) minmax(0,1fr)` pair all the way down. Inside
     a ~330px card the 224px track plus the 30px gap leaves nothing for the
     drawing, and the SVG ran 443px past the card at 390px wide -- clipped, not
     scrollable, because `.afig--stage` is `overflow: hidden`. The stage variant
     has to be named here too or it never collapses. */
  .afig-atomic,
  .afig--stage .afig-atomic { grid-template-columns: 1fr; gap: 22px; }
  .atomic-svg { width: 100%; }
}

/* ---- C. four phases of adoption ---- */
/* ---- ROWS, not columns ----
   `#implementation` at the foot of the enterprise page is the reference: a
   mono index in a left gutter, a title, a body, and one hairline between steps.
   Four columns of prose forced 15px copy into ~200px tracks once the figure
   moved onto a card, and a step SEQUENCE reads down a list, not across a row --
   the columns were also the only thing on the page claiming four equal weights
   for what is a progression. */
.afig-phases {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
}

.phase {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr);
  column-gap: 16px;
  padding: 12px 0;
  border-top: 1px solid var(--fig-line);
}

.phase:first-child {
  padding-top: 0;
  border-top: 0;
}

/* The last row used to drop its bottom padding, and the scale below then added
   another 20px of its own margin, so the hairline over the scale sat 37px under
   the last line where every other hairline in the card sits 12px under the line
   above it. The row keeps its padding and the scale sits flush, so one rhythm
   runs from the first row to the wedge. */

/* Four solid --blue chips in a row was the third big blue fill on the page, and
   the site has no filled chip at all. The pattern index.html uses for a column
   inside a card is a KICKER ROW plus a title: a mono index at --fs-stamp, blue
   because here the stamp IS the signal (§15.6), over an in-card title at
   --fs-card-ttl. Same anatomy as `.solution-kicker` + the card h3 beneath it,
   one step down in scale because four of them share a row. */
.phase-chip {
  display: contents;
}

/* the gutter index: mono, blue because the stamp IS the signal here (§15.6),
   10px because it is inside a card. Same slot `#implementation` puts `01` in. */
.phase-chip b {
  grid-column: 1;
  align-self: baseline;
  color: var(--blue);
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

.phase-chip span {
  grid-column: 2;
  display: block;
  color: var(--fig-ink);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.01em;
  line-height: 1.2;
}

/* the role sits on the title's own line, right-aligned, the way `.al-step .mt`
   carries a row's meta -- it is a classification, not a second heading */
/* 4px here and 7px on the paragraph below made two different gaps inside a row
   that reads as one block. Both are 6px, so the three lines of a phase are
   evenly set and the rows stack on one rhythm. */
.phase-role {
  grid-column: 2;
  margin-top: 6px;
  color: var(--fig-muted);
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.35;
}

.phase p {
  grid-column: 2;
  margin-top: 6px;
  color: var(--fig-muted);
  font-size: 12.5px;
  line-height: 1.5;
}

.phase p b { color: var(--fig-text); font-weight: 500; }

/* Manual effort falls as trust rises. Two right triangles sharing one
   hypotenuse, so together they tile a single rectangle: at the left edge the
   graphite wedge is the full height and the blue is nothing, at the right edge
   the reverse. Reading a fixed total means the two quantities trade against
   each other, which two crossing hairlines could not say -- they only showed
   two independent slopes and left the space between them meaningless. Each
   label sits inside its own wedge at that wedge's thick end, so no legend is
   needed, and the three white seams fall on the phase-column separators above,
   so the scale is measured by the same grid. */
.phase-scale {
  position: relative;
  /* flush: the last row's own 12px bottom padding is the space above this rule,
     the same 12px every other row leaves above the rule under it */
  margin-top: 0;
  border-top: 1px solid var(--fig-line);
  padding-top: 20px;
}

.phase-scale svg {
  display: block;
  width: 100%;
  height: 76px;
}

/* The one place a large blue fill is right: blue is a measured quantity here,
   the same job it does in a progress bar. Its counterpart takes --cream-2, the
   palette's neutral light surface, instead of an ad-hoc navy tint. */
.phase-scale .ps-fall { fill: var(--fig-tile-2); }
.phase-scale .ps-rise { fill: var(--blue); }

.phase-scale .ps-grid line {
  stroke: #fff;
  stroke-opacity: .8;
  stroke-width: 1.5;
}

.phase-scale .ps-a,
.phase-scale .ps-b {
  position: absolute;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: -.005em;
}

/* pinned inside the wedges: the graphite one is thickest top left, the blue
   one bottom right. 38px = the svg's own 26px top offset plus a 12px inset. */
.phase-scale .ps-a { top: 31px; left: 12px; color: var(--fig-ink); }
.phase-scale .ps-b { right: 12px; bottom: 11px; color: #fff; }

@media (max-width: 720px) {
  /* already one column; the quarter seams would divide nothing at this width */
  /* the row is no longer four columns, so quarter seams would divide nothing */
  .phase-scale .ps-grid { display: none; }
}

@media (max-width: 460px) {
  .phase { grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; }
}

/* Squared cells that touch each other and the rails on both sides */
/* index.html's card kicker is a ROW: the eyebrow left, its index right, both in
   the eyebrow's own face and colour (`.solution-kicker` + `.solution-kicker
   .number`). These four cells are an ordered sequence and carried no index at
   all, so the counter supplies one and the cell head becomes that row. */
.article-summary {
  --summary-rule: 1px solid var(--art-hair);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin: 40px 0 0;
  border-top: var(--summary-rule);
  border-left: var(--summary-rule);
  background: #fff;
  box-shadow:
    0 1px 2px rgba(10, 16, 60, .05),
    0 6px 14px rgba(10, 16, 60, .04),
    0 20px 46px rgba(10, 16, 60, .08);
  counter-reset: summary;
}

/* 170px reserved two dead lines under every cell: the longest of the four
   bodies is one line at this measure. Content-driven with an even 30px inset
   instead, so the four cells stay identical without the void. */
.article-summary > div {
  position: relative;
  padding: 30px;
  border-right: var(--summary-rule);
  border-bottom: var(--summary-rule);
  counter-increment: summary;
}

/* the index sits on the kicker's own baseline, at its right, in its face */
.article-summary > div::after {
  position: absolute;
  top: 30px;
  right: 30px;
  color: var(--blue);
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: .14em;
  content: counter(summary, decimal-leading-zero);
}

/* A card kicker paired with a card title is the eyebrow at 11px -- §15.6 names
   this exact case, and index.html's .solution-kicker is the reference. Uppercase
   and .14em are what make it a kicker rather than a bold first word; without
   them a 12.5px blue sans label is a third device the site does not own. */
.article-summary b {
  display: block;
  margin-bottom: 12px;
  color: var(--blue);
  font-family: var(--sans);
  /* the eyebrow's 11px card-kicker step (§15.6). No sans 11px token exists, so
     it borrows --fs-stamp's size and keeps the eyebrow's family and tracking. */
  font-size: var(--fs-stamp);
  font-weight: 400;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* the card title: display face at h3 scale (§16.4), not a fixed 21px */
.article-summary strong {
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.012em;
  line-height: 1.14;
}

.article-summary p {
  margin-top: 10px;
  color: var(--muted);
  font-size: var(--fs-body);
  line-height: 1.55;
}

/* the sentence that closes the cadence, spaced off the grid it summarises
   (was an inline `style="margin-top:28px"` on the page) */
.article-body > .article-summary + p {
  margin-top: 28px;
}

.article-sources {
  display: grid;
  grid-template-columns: minmax(0, 1fr) min(760px, 100%) minmax(0, 1fr);
  margin-top: clamp(72px, 9vw, 120px);
  padding-top: 34px;
  border-top: 1px solid var(--art-hair);
}

.article-sources > * {
  grid-column: 2;
}

/* an in-component title, so it is the stamp, muted: it inherited the h2's ink
   and read as heavy as a headline for a label that is pure apparatus */
.article-sources h2 {
  margin: 0 0 18px;
  color: var(--muted);
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

/* A numbered reference list is the site's hairline row again: index at the left
   in the mono stamp, rule under every row, --fs-meta muted copy. It was a
   browser-default `<ol>` -- proportional digits, a 22px marker gutter, 14px
   gaps and no rules -- which is the one list on the page that looked unstyled.
   `decimal-leading-zero` because every index on the site is two digits. */
.article-sources ol {
  display: grid;
  gap: 0;
  padding-left: 0;
  border-top: 1px solid var(--art-hair);
  counter-reset: fn;
  list-style: none;
}

/* Normal flow with the index in the gutter, NOT a two-track grid: a reference
   is inline content -- text, links and a back-arrow -- so as grid items each run
   of text became its own cell and every source set one word per line in a 30px
   column. */
.article-sources li {
  position: relative;
  padding: 13px 0 13px 42px;
  border-bottom: 1px solid var(--art-hair);
  counter-increment: fn;
  color: var(--muted);
  font-size: var(--fs-meta);
  line-height: 1.55;
}

/* Every tick property has to be unset explicitly. `.article-body li::before`
   paints the blue tick as a masked 17px box at equal specificity, so overriding
   only `display` and `content` left the mask and the absolute box in place and
   each reference grew a tick beside its number. */
.article-sources li::before {
  display: block;
  position: absolute;
  top: 15px;
  left: 0;
  width: auto;
  height: auto;
  background: none;
  -webkit-mask: none;
  mask: none;
  color: var(--muted);
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  letter-spacing: .13em;
  content: counter(fn, decimal-leading-zero);
}

/* Rules run rail to rail; the label and the arrow return to the text column,
   the arrow pinned to its right edge so it lines up with the copy above. */
.article-next {
  display: grid;
  grid-template-columns: minmax(0, 1fr) min(760px, 100%) minmax(0, 1fr);
  align-items: center;
  margin-top: 72px;
  padding: 32px 0;
  border-block: 1px solid var(--art-hair);
}

.article-next > div {
  grid-area: 1 / 2;
  justify-self: start;
  padding-right: 68px;
}

.article-next > a {
  grid-area: 1 / 2;
  justify-self: end;
}

/* a signpost above a headline is the eyebrow (§15.6) */
.article-next span {
  display: inline-block;
  color: var(--blue);
  font-family: var(--sans);
  font-size: var(--fs-eyebrow);
  font-weight: 400;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.article-next h3 {
  margin: 10px 0 0;
  font-size: var(--fs-h3);
  line-height: 1.14;
}

/* ---- a link to an article carries that article's listing image ----------
   Requested rule: whenever we link to an article, show the image it has on the
   resources overview, so the card and the link are the same object in two
   places. The thumbnails are `.article-thumb` + a `.thumb-*` motif, already
   defined below for the listing grid, so this only sizes one for a row and
   scales the motif's internal padding down with it.

   `.article-next span` is the eyebrow at descendant specificity, so the copy
   wrapper here must not be a <span> or it inherits blue uppercase. Divs. */
.article-next .an-row {
  display: flex;
  align-items: center;
  gap: 22px;
}

.article-next .an-thumb {
  flex: none;
  width: 150px;
  aspect-ratio: 16 / 9;
  overflow: hidden;
}

.article-next .an-copy { min-width: 0; }

/* the motifs are drawn for a ~360px card, so their padding and gaps are
   proportionally huge at 150px. Scaled, not redrawn. */
.article-next .an-thumb .thumb-system,
.article-next .an-thumb .thumb-five {
  padding: 12px;
  gap: 6px;
}

.article-next .an-thumb .thumb-five i::after { height: 2px; }

/* .thumb-slots and .thumb-bars draw in ABSOLUTE units tuned for the 556x313
   listing card: an 18px pad, a 30px slot stack on a 10px pitch, 34px bars 7px
   wide. The row thumb is 150x84, 27% of that, so at full size the pad ate the
   columns and the stacks rendered as loose white chips -- the link showed a
   different mark from the one the listing shows, which is the whole point of
   the pair. Scaled here rather than in the motif, and hand-set rather than
   multiplied through: a literal 27% puts the slot pitch at 2.7px and the bar
   at 1.6px, under the point where either survives. The floors are 3px of ink
   on a 4px pitch, so the marks stay the same drawing, one step coarser. */
.article-next .an-thumb .thumb-slots,
.article-next .an-thumb .thumb-bars {
  padding: 9px;
}

.article-next .an-thumb .thumb-slots i::after {
  right: 3px;
  bottom: 26%;
  left: 3px;
  height: 15px;
  background:
    linear-gradient(#fff, #fff) left calc(var(--on, 0) * 4px) / 100% 3px no-repeat,
    repeating-linear-gradient(180deg, rgba(255, 255, 255, .42) 0 1px, transparent 1px 4px)
      left top / 100% 16px no-repeat;
}

.article-next .an-thumb .thumb-bars i::after {
  /* the thirds the full-size mark uses; 1.5px is half a bar here */
  right: calc(100% / 6 - 1.5px);
  bottom: 24%;
  left: calc(100% / 6 - 1.5px);
  height: 16px;
  background:
    linear-gradient(0deg, #fff, #fff) center bottom / 3px max(10px, calc(var(--w, 3) * 3.6px)) no-repeat,
    linear-gradient(0deg, rgba(255, 255, 255, .40), rgba(255, 255, 255, .40)) left bottom / 3px 6px no-repeat,
    linear-gradient(0deg, rgba(255, 255, 255, .40), rgba(255, 255, 255, .40)) right bottom / 3px 8px no-repeat;
}

/* .thumb-loop places its four satellites with ABSOLUTE box-shadow offsets
   (+/-84px, +/-64px) and an absolute -27px spread, tuned for the ~360px listing
   card. In a 150x84 row thumb the box is 75x42 from its centre, so every
   satellite falls outside it and is clipped by overflow:hidden -- the link showed
   a bare ring, which is not the mark the listing shows. The ring is a percentage
   and already scales; the offsets and the spread are what have to come down.
   These values also clear the 190x107 thumb the mobile column uses. */
.article-next .an-thumb .thumb-loop::after {
  border-width: 1.5px;
  box-shadow:
    -46px 0 0 -22px rgba(255, 255, 255, .25),
     46px 0 0 -22px rgba(255, 255, 255, .25),
    0 -29px 0 -22px rgba(255, 255, 255, .25),
    0  29px 0 -22px rgba(255, 255, 255, .25);
}

/* Below 620px the text column is ~230px and `.article-next > div` already
   reserves 68px of it for the arrow. A thumb, a headline and the arrow cannot
   share the remaining 162px -- measured, the copy ran under the arrow. The
   image is never dropped, so the row becomes a column. */
@media (max-width: 620px) {
  .article-next .an-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
  }

  .article-next .an-thumb { width: min(100%, 190px); }
}

/* The one arrow-button recipe on the site: 44px, radius 8px, white on a
   hairline, hover fill, press to .94 (DESIGN-BOOK §5, and the same rule as
   #results .tstmnl-nav button on the home page). This one was a hard square. */
.article-next a {
  display: grid;
  width: 44px;
  height: 44px;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
  color: #3A3A3A;
  text-decoration: none !important;
  transition:
    background .2s var(--ease-micro),
    border-color .2s var(--ease-micro),
    color .2s var(--ease-micro),
    transform .2s var(--ease-micro);
}

.article-next a:hover {
  background: #F3F3F3;
  border-color: #E5E5E5;
  color: var(--ink);
}

.article-next a:active {
  transform: scale(.94);
}

/* Five-shift editorial graphic */
/* `.article-visual` is `display: grid; place-items: center` with a reserved
   min-height, so this graphic is a centred grid ITEM: `min-height: inherit`
   resolved to the stage's own 588px inside a stage that also has 56px of
   padding, which pushed the frame 112px taller than it reserved. Stretching in
   the block axis instead makes the five vertical hairlines run the full inner
   height, which is the point of them. */
.five-shifts-graphic {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  /* index, slack, rule, title, sub. Five named rows on the PARENT, so the five
     columns share them: the rule track is one track, not five, and it lands on
     one line whatever the columns contain. */
  grid-template-rows: auto minmax(0, 1fr) auto auto auto;
  width: min(1120px, 100%);
  align-self: stretch;
  justify-self: center;
  align-items: stretch;
}

/* Bottom-packing each column separately looked right only while every column
   carried the same number of lines. "Segments of one" has a two-line sub where
   the other four have three, so its whole stack, rule included, sat 19.5px
   below the rest. Subgrid removes the coincidence: each column adopts the
   parent's five rows instead of packing its own, so the rule sits in one shared
   track and cannot drift with the length of the text under it. */
.five-shifts-graphic article {
  display: grid;
  grid-row: 1 / -1;
  grid-template-rows: subgrid;
  padding: 32px 22px 34px;
  border-left: 1px solid rgba(255, 255, 255, .24);
}

.five-shifts-graphic article > b { grid-row: 1; }
.five-shifts-graphic article > i { grid-row: 3; }
.five-shifts-graphic article > h2 { grid-row: 4; }
.five-shifts-graphic article > p { grid-row: 5; }

.five-shifts-graphic article:last-child {
  border-right: 1px solid rgba(255, 255, 255, .24);
}

/* On artwork the site sets a stamp at --fs-stamp in rgba(255,255,255,.72) --
   `.stage-stamp` and `.al-*` both do. This was solid white at .14em, which made
   five indices as loud as the five titles under them. */
.five-shifts-graphic b {
  /* the slack is row 2 now, not an auto margin */
  color: rgba(255, 255, 255, .72);
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  font-weight: 400;
  letter-spacing: .13em;
}

/* A hairline that touches the text it labels (§4.2 rule 4), not a 30x3 white
   dash: the site draws no dash anywhere, and this one sat in space above the
   title. Full column width, so it lines up with the vertical rails beside it
   and the reveal draws it in from the left. */
.five-shifts-graphic i {
  width: 100%;
  height: 1px;
  margin-bottom: 20px;
  background: rgba(255, 255, 255, .34);
}

/* The shared row already reserves whatever the tallest title needs, so the
   min-height that used to hard-code "two lines" is gone. The bottom alignment
   stays: a one-line title sits at the bottom of the shared track, which is what
   puts every title's last line on one baseline. */
.five-shifts-graphic h2 {
  display: flex;
  align-items: flex-end;
  color: #fff;
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.01em;
  line-height: 1.06;
}

.five-shifts-graphic p {
  margin-top: 12px;
  color: rgba(255, 255, 255, .72);
  font-size: var(--fs-meta);
  line-height: 1.5;
}

/* ============================================================================
   Welcome-to-uplane editorial graphic: the three eras, drawn as resolution

   Every article's hero motif and its listing thumbnail are the same idea at two
   scales (.thumb-five, .thumb-loop, .thumb-system). Welcome had neither: its
   headline sat inside a photograph and its listing card was a bare photo, the
   only one in the grid without a drawn mark.

   The motif states the article's own argument. Its middle section tracks three
   operating models of increasing precision -- one message for everyone, then
   targeted segments with generic creative, then individual relevance -- so the
   graphic draws precision itself: one bar, then four, then a dot field. The
   marks are the same object subdividing, which is what "increasing resolution"
   looks like, and it reads at 340px as well as at 1400px.

   Vocabulary is the reference's, deliberately, so the two heroes agree: mono
   index at --fs-stamp in rgba(255,255,255,.72), a full-column-width 1px rule at
   .34, vertical hairlines at .24, serif title on --fs-h3 bottom-aligned in a
   reserved two-line box, muted sub-line on --fs-meta.
   ========================================================================= */
.eras-graphic {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* header, then the column's own six rows: index, slack, mark, rule, title,
     sub. They live on the PARENT so the three columns share them. Packed
     per column instead, the rule drifted with the length of the sub under it:
     at 1000px the three rules sat at 521.5, 543.7 and 524.2, three lines where
     the drawing needs one. */
  grid-template-rows: auto auto minmax(0, 1fr) auto auto auto auto;
  width: min(1120px, 100%);
  align-self: stretch;
  justify-self: center;
  align-items: stretch;
}

/* The title the motif was missing. It spans the grid and sits above the column
   rules, so it names the comparison rather than belonging to the first column;
   its left edge is the first column's text edge (26px padding + the 1px rule),
   not the graphic's box edge. */
.eras-head {
  grid-column: 1 / -1;
  padding: 4px 26px 30px 27px;
}

.eras-stamp {
  display: block;
  margin-bottom: 14px;
  color: #fff;
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

.eras-head h2 {
  max-width: 22ch;
  color: #fff;
  font-family: var(--serif);
  font-size: var(--fs-h2);
  font-weight: 500;
  letter-spacing: -.015em;
  line-height: 1.04;
}

@media (max-width: 900px) {
  .eras-head { padding-bottom: 22px; }
  .eras-head h2 { max-width: none; }
}

.eras-graphic article {
  display: grid;
  grid-row: 2 / -1;
  grid-template-rows: subgrid;
  padding: 32px 26px 34px;
  border-left: 1px solid rgba(255, 255, 255, .24);
}

.eras-graphic article > b { grid-row: 1; }
.eras-graphic article > .eras-viz { grid-row: 3; }
.eras-graphic article > .rule { grid-row: 4; }
.eras-graphic article > h2 { grid-row: 5; }
.eras-graphic article > p { grid-row: 6; }

.eras-graphic article:last-child {
  border-right: 1px solid rgba(255, 255, 255, .24);
}

/* Full strength, not the reference's .72.
   Measured on the real composited pixels (tmp/motifcontrast.mjs), an 11px mono
   index at .72 white reads 3.1-4.6:1 against §16.3's 5.7:1 floor, and
   tmp/artbands.mjs shows that is true of EVERY band artwork in the library --
   including 19.webp, which the reference itself uses. The opacity is the binding
   constraint, not the photograph. At full white the same index reads 8.7:1 at
   desktop widths. Hierarchy against the 26px serif title below it comes from
   size, face and case, which is enough: the two are never confusable. */
.eras-graphic b {
  /* the slack is row 2 of the shared grid now, not an auto margin */
  color: #fff;
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  font-weight: 400;
  letter-spacing: .13em;
}

/* The three marks, drawn with background-image rather than child elements, so a
   column carries no markup that exists only to be a shape. One 28px block each,
   so the three sit on one baseline and only their density differs. */
/* All three top-align in the same 25px band, so the first mark of every column
   shares one line and only the density below it changes. Centring the single bar
   instead put it 10px below the first of the four beside it, and the eye compares
   first marks, not optical centres. */
.eras-viz {
  display: block;
  width: 100%;
  height: 25px;
  margin: 26px 0 20px;
}

/* one message for the entire market: one mark, and nothing under it */
.eras-viz.is-one {
  background: linear-gradient(#fff, #fff) left top / 100% 3px no-repeat;
}

/* sophisticated targeting, generic creative: the same bar, subdivided */
.eras-viz.is-few {
  /* an 8px period puts FOUR marks in the 25px band. At 11px it drew three, which
     read as the same count as the three columns and muddied the step. */
  background: repeating-linear-gradient(
    180deg,
    rgba(255, 255, 255, .92) 0 3px,
    transparent 3px 8px
  ) left top / 100% 25px no-repeat;
}

/* individual relevance: the field resolves to one mark per person */
.eras-viz.is-many {
  background: radial-gradient(rgba(255, 255, 255, .92) 1.2px, transparent 1.4px)
    left top / 9px 9px;
}

.eras-graphic .rule {
  display: block;
  width: 100%;
  height: 1px;
  margin-bottom: 20px;
  background: rgba(255, 255, 255, .34);
}

/* the shared row reserves whatever the tallest title needs; the bottom
   alignment is what puts every title's last line on one baseline */
.eras-graphic h2 {
  display: flex;
  align-items: flex-end;
  color: #fff;
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.01em;
  line-height: 1.06;
}

.eras-graphic p {
  margin-top: 12px;
  color: rgba(255, 255, 255, .92);
  font-size: var(--fs-meta);
  line-height: 1.5;
}

/* The device §14.9 kept for a label on artwork once the scrim idea was dropped:
   the type carries its own shadow and the photograph stays a photograph. Note it
   does NOT show up in the contrast numbers -- the harness hides the text to read
   the artwork, which hides the shadow with it -- so it is a real perceptual gain
   the measurement cannot credit. */
.eras-graphic b,
.eras-graphic h2,
.eras-graphic p,
.eras-stamp {
  text-shadow: 0 1px 12px rgba(3, 8, 35, .45);
}

/* ============================================================================
   Resource-card motifs at article scale

   These two stages deliberately use the same photograph, navy wash, and white
   drawing as their cards on the Resources page. The larger treatment stays as
   restrained as the thumbnail: no mock product interface, no floating card,
   no explanatory copy inside the artwork, and no animated ornament.
   ========================================================================= */
.article-visual--card-motif::before {
  background:
    linear-gradient(145deg, rgba(7, 20, 97, .58), rgba(6, 16, 80, .20) 52%, rgba(7, 20, 97, .56)),
    var(--visual-img, radial-gradient(circle at 80% 20%, rgba(35, 185, 255, .78), transparent 34%))
      center / cover no-repeat,
    linear-gradient(145deg, #071461, #1748ff 58%, #0ba9dd);
}

/* Beyond the campaign: the listing card's static loop, enlarged without the
   extra orbit, connector spokes, centre square, or travelling stroke that made
   the previous masthead feel like a loading animation. */
.campaign-mark {
  position: relative;
  width: min(76%, 470px);
  aspect-ratio: 1;
}

.campaign-ring {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 38%;
  aspect-ratio: 1;
  border: 2px solid #fff;
  border-radius: 50%;
  transform: translate(-50%, -50%);
}

.campaign-point {
  position: absolute;
  width: clamp(32px, 9.8%, 46px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(255, 255, 255, .25);
}

.campaign-point.point-north,
.campaign-point.point-south {
  left: 50%;
  transform: translateX(-50%);
}

.campaign-point.point-north { top: 21%; }
.campaign-point.point-south { bottom: 21%; }

.campaign-point.point-east,
.campaign-point.point-west {
  top: 50%;
  transform: translateY(-50%);
}

.campaign-point.point-east { right: 17%; }
.campaign-point.point-west { left: 17%; }

/* Continuous learning: the Resources card's three operating plates. Their
   equal crop and common baseline make the system feel ordered; the white,
   lifted centre plate is the single point of emphasis from the thumbnail. */
.learning-mark {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 2.2vw, 24px);
  align-items: center;
  width: min(76%, 780px);
}

.learning-mark i {
  position: relative;
  width: 100%;
  aspect-ratio: .78;
  border: 1px solid rgba(255, 255, 255, .42);
  background: rgba(255, 255, 255, .12);
}

.learning-mark i:nth-child(2) {
  border-color: #fff;
  background: #fff;
  transform: translateY(clamp(-24px, -2.2vw, -12px));
}

@media (max-width: 560px) {
  .article-visual--card-motif { min-height: 520px; }
  .campaign-mark { width: min(100%, 340px); }
  .learning-mark { width: 100%; }
}

/* ============================================================================
   HERO MOTIF FRAME, shared (DESIGN-BOOK §30, §31)

   `.five-shifts-graphic` and `.eras-graphic` each implement this frame privately.
   The two motifs added after them share ONE implementation instead, because four
   private copies of the same grid is how the label devices drifted in the first
   place. The two older ones are left alone deliberately: five-shifts is the
   approved reference and folding it in would risk its rendering for no gain.

   The frame is: columns divided by white hairlines, a mono index per column, one
   MARK, a full-column rule, a serif title bottom-aligned in a reserved two-line
   box, and a muted sub-line. Only the mark changes between articles -- that is
   what keeps four heroes recognisably one family while still letting each state
   its own argument.

     five-shifts   a plain rule            (five connected shifts)
     welcome       density: 1 bar, 4, many (three eras of precision)
     engagement    one filled square advancing along a row of empty ones
                   (the decision happens at the customer's moment)
     testing       a small bar chart with one bar solid
                   (many variants, one promoted)

   Type is at FULL strength, not .72. §30.4: no band artwork in the library clears
   §16.3's 5.7:1 for an 11px mono index at .72, including the reference's own.
   ========================================================================= */
.hm-grid {
  display: grid;
  width: min(1120px, 100%);
  align-self: stretch;
  justify-self: center;
  align-items: stretch;
}

.hm-grid.is-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }

.hm-grid > article {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 32px 22px 34px;
  border-left: 1px solid rgba(255, 255, 255, .24);
}

.hm-grid > article:last-child {
  border-right: 1px solid rgba(255, 255, 255, .24);
}

.hm-grid b {
  margin-bottom: auto;
  color: #fff;
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  font-weight: 400;
  letter-spacing: .13em;
}

.hm-mark {
  width: 100%;
  height: 34px;
  margin: 20px 0 18px;
}

.hm-grid .rule {
  width: 100%;
  height: 1px;
  margin-bottom: 20px;
  background: rgba(255, 255, 255, .34);
}

.hm-grid h2 {
  display: flex;
  align-items: flex-end;
  min-height: 2.12em;
  color: #fff;
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.01em;
  line-height: 1.06;
}

.hm-grid p {
  margin-top: 12px;
  color: rgba(255, 255, 255, .92);
  font-size: var(--fs-meta);
  line-height: 1.5;
}

/* Each card was its own flex column bottom-anchored to the frame, so the only
   thing guaranteed to line up across the five was the last line of the last
   paragraph. Everything above it floated on its own content: a three-line
   headline pushed that card's rule and mark a full line higher than its
   neighbours, and the row of marks -- the motif this hero is built on -- read as
   ragged. Subgrid puts all five cards on the SAME five rows, so the kicker, the
   mark, the rule, the headline and the paragraph each sit at one height across
   the whole band whatever the copy does. Row 2 is the flexible one, which is
   what `b { margin-bottom: auto }` was doing by hand.
   Both articles that carry this motif have exactly five children per card. */
@supports (grid-template-rows: subgrid) {
  .hm-grid { grid-template-rows: auto 1fr auto auto auto; }

  .hm-grid > article {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 5;
  }

  .hm-grid b { margin-bottom: 0; }
  .hm-mark { align-self: end; }

  /* the rows equalise now, so the headline no longer reserves two lines of its
     own, and it sets from the TOP of its row: with the rule fixed above it, a
     shorter headline should leave its slack under the type rather than open a
     gap between the rule and the first line */
  .hm-grid h2 {
    align-items: flex-start;
    min-height: 0;
  }
}

/* §14.9's device for type on artwork: the glyphs carry their own shadow and the
   photograph stays a photograph. Not visible to the contrast harness, which hides
   the type to read the artwork and hides the shadow with it. */
.hm-grid b,
.hm-grid h2,
.hm-grid p {
  text-shadow: 0 1px 12px rgba(3, 8, 35, .45);
}

/* ---- mark A: one filled square advancing along a row of empty ones ----
   Beyond the campaign. Its claim is that the decision belongs to the customer's
   moment rather than to a schedule, so the mark is POSITION: six slots, one of
   them live, and the live one is somewhere different in every column. Squares
   because the square is the brand's mark; a row because the axis is time. */
.hm-slots {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 4px;
  /* bottom-aligned, so the slots sit on the same line the bars stand on */
  align-items: flex-end;
  height: 34px;
}

.hm-slots i {
  height: 7px;
  border: 1px solid rgba(255, 255, 255, .42);
}

.hm-slots i.on {
  background: #fff;
  border-color: #fff;
}

/* ---- mark B: a small bar chart with one bar solid ----
   From production to continuous learning. Its claim is that a cycle tests many
   variants and promotes one, so the mark is a MEASUREMENT: five bars at real
   heights with the winner filled. This is §15.4's chart language at motif scale,
   which is why it reads as evidence rather than as decoration. */
/* 34px tall and 9px wide, not 22px tall and a fifth of the column wide. At the
   shared mark height the "bars" were 40x8 horizontal dashes and the 38-78% spread
   resolved to 8-17px, so it read as a row of ticks rather than as a measurement.
   A chart needs the vertical axis to be the long one. */
.hm-bars {
  display: grid;
  grid-template-columns: repeat(5, 9px);
  justify-content: start;
  gap: 5px;
  align-items: flex-end;
  height: 34px;
}

.hm-bars i {
  height: var(--h, 40%);
  background: rgba(255, 255, 255, .38);
}

.hm-bars i.on {
  background: #fff;
}

/* Both hiding rules stop at 641px. Dropping a column was the only way to keep
   the titles readable while the five cards had to share one screen at once;
   below the phone breakpoint they no longer do -- the band is a swipe track
   there and every card gets the full width -- and hiding three of five hero
   points on the device most people read the article on was content loss, not a
   layout decision. */
@media (max-width: 900px) and (min-width: 641px) {
  .hm-grid.is-5 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* four and five would each be a 60px column: the title cannot set in that */
  .hm-grid.is-5 > article:nth-child(n+4) { display: none; }
}

/* ============================================================================
   The agent-log card on an article stage (DESIGN-BOOK §6.5, §7)

   Replaces `.system-graphic`: three warm cards side by side, each holding a
   drawn mock of a UI -- gradient "variant" thumbnails, rounded score bars, two
   empty boxes labelled PEOPLE and AGENTS. §7 forbids exactly that ("never use
   dashboard screenshots, glassy mock UIs, or illustration"), its labels ran
   9-10px against the 10px in-card floor (§15.6), its radii were 3 / 4 / 5 / 8px,
   and three cards on one frame is not the language: a stage carries ONE card.

   The card itself comes from assets/alog.css unchanged, so there is nothing to
   restyle here. What this block does is the two things a stage owes it: keep the
   frame larger than the card, and light the card for artwork rather than for the
   white page (§7.1's light-surface note in reverse -- here the surround is dark,
   so the card keeps its full shadow and drops the hairline).
   ========================================================================= */
/* §7.1's card is `min(86%, 540px)`, which is right on the ~700px frames it was
   written for. On a 1400px full-bleed masthead 540px is 39% of the stage, where
   every other card on the site sits at 74-86% of its frame and reads as the
   subject rather than as an object dropped in a field. A masthead cannot get
   narrower, so the card gets wider: 640px is 46% at 1400px and still leaves the
   stage 62px of clearance above and below (tmp/stagefit.js). */
.article-visual .alog {
  width: min(86%, 640px);
  box-shadow:
    0 2px 6px rgba(6, 10, 40, .10),
    0 36px 90px rgba(6, 10, 40, .34);
}

/* alog.css styles `.al-logo svg` but not `.al-logo img`, and the LinkedIn mark
   is the one channel logo that ships as a file rather than an inline path. */
.article-visual .al-logo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The card's own steps are the graphic, so the sub-row's connector line has to
   read on warm paper rather than on the page: alog.css already sets it, this
   only keeps the row from touching the card wall at the narrow end. */
@media (max-width: 560px) {
  .article-visual .alog {
    width: 100%;
    padding: 20px 18px 18px;
  }

  .article-visual .al-sub {
    margin-left: 22px;
  }

  .article-visual .al-sub::before {
    left: -15px;
  }
}

/* the stamp bottom-left of the stage, mono, on artwork (§6.5). Positioned off
   the stage's own padding so it sits on the same inset as the card. */
.article-visual .stage-stamp {
  position: absolute;
  right: auto;
  bottom: clamp(16px, 2.4vw, 26px);
  left: clamp(20px, 2.6vw, 30px);
  z-index: 3;
  /* Pure white, not the 85-88% the other stages use. Measured on the real
     composited pixels (tmp/artstamp.js) the artwork under this stamp sits at
     luma .108, its brightest at 1512px: white reads 6.65:1 there and 88% white
     reads 5.1:1, under §16.3's 5.7:1 floor. The alternative was a scrim over
     the foot of the stage, which §14.12 already tried on the hero ticker and
     reverted -- it just made the artwork look grey. */
  color: #fff;
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  letter-spacing: .13em;
  text-transform: uppercase;
  white-space: nowrap;
}

.article-visual .stage-stamp .sep {
  /* site.css has a bare `span` rule at element specificity (§19) */
  margin: 0 8px;
  font-family: inherit;
  opacity: .5;
}

@media (max-width: 560px) {
  .article-visual .stage-stamp {
    white-space: normal;
    line-height: 1.5;
  }
}

/* Resources card thumbnails */
.article-thumb {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #1230e6;
  color: #fff;
}

/* The backdrop behind the drawn motif. Cards pass a photo through --thumb-img
   (`--thumb-img: url(...)` inline) so the thumbnails sit on the same imagery as
   the photographic cards; the flat brand gradient is the fallback. The tint on
   top is what keeps the white line-art legible wherever a photo goes bright. */
.article-thumb::before {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(145deg, rgba(7,20,97,.58), rgba(6,16,80,.20) 52%, rgba(7,20,97,.56)),
    var(--thumb-img, radial-gradient(circle at 80% 20%, rgba(35,185,255,.78), transparent 34%))
      center / cover no-repeat,
    linear-gradient(145deg, #071461, #1748ff 58%, #0ba9dd);
  content: "";
}

.thumb-five {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  padding: 18px;
}

.thumb-five i {
  position: relative;
  border-left: 1px solid rgba(255,255,255,.34);
}

.thumb-five i:last-child {
  border-right: 1px solid rgba(255,255,255,.34);
}

.thumb-five i::after {
  position: absolute;
  right: 5px;
  bottom: calc(8% + var(--n) * 3%);
  left: 5px;
  height: 3px;
  background: #fff;
  content: "";
}

/* The miniature of .eras-graphic: the same three marks at the same three
   densities, on the same hairline columns. It is the motif, not a smaller
   drawing of it -- which is what makes a card and its article read as one thing
   in the grid. Welcome's card was the only bare photograph among four drawn
   marks. */
.thumb-eras {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  padding: 18px;
}

.thumb-eras i {
  position: relative;
  border-left: 1px solid rgba(255,255,255,.34);
}

.thumb-eras i:last-child {
  border-right: 1px solid rgba(255,255,255,.34);
}

.thumb-eras i::after {
  position: absolute;
  right: 9px;
  bottom: 22%;
  left: 9px;
  height: 22px;
  content: "";
}

.thumb-eras i:nth-child(1)::after {
  background: linear-gradient(#fff, #fff) left center / 100% 3px no-repeat;
}

.thumb-eras i:nth-child(2)::after {
  background: repeating-linear-gradient(
    180deg,
    rgba(255,255,255,.92) 0 3px,
    transparent 3px 10px
  ) left top / 100% 23px no-repeat;
}

.thumb-eras i:nth-child(3)::after {
  background: radial-gradient(rgba(255,255,255,.92) 1.1px, transparent 1.3px)
    left top / 8px 8px;
}

/* The miniature of the slots motif: five hairline columns, each with one live
   slot in a different place. Replaces `.thumb-loop`, a ring with four soft
   satellites -- which read as a campaign cycle, the thing the article argues
   against, and which was the only circle in a grid of squared marks. */
.thumb-slots {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  padding: 18px;
}

.thumb-slots i {
  position: relative;
  border-left: 1px solid rgba(255,255,255,.34);
}

.thumb-slots i:last-child {
  border-right: 1px solid rgba(255,255,255,.34);
}

/* four stacked slots per column, the live one set by --on */
.thumb-slots i::after {
  position: absolute;
  right: 7px;
  bottom: 26%;
  left: 7px;
  height: 30px;
  background:
    linear-gradient(#fff, #fff) left calc(var(--on, 0) * 10px) / 100% 6px no-repeat,
    repeating-linear-gradient(180deg, rgba(255,255,255,.42) 0 1px, transparent 1px 10px)
      left top / 100% 31px no-repeat;
  content: "";
}

.thumb-loop {
  display: grid;
  place-items: center;
}

.thumb-loop::after {
  position: relative;
  width: 38%;
  aspect-ratio: 1;
  border: 2px solid #fff;
  border-radius: 50%;
  box-shadow:
    -84px 0 0 -27px rgba(255,255,255,.25),
    84px 0 0 -27px rgba(255,255,255,.25),
    0 -64px 0 -27px rgba(255,255,255,.25),
    0 64px 0 -27px rgba(255,255,255,.25);
  content: "";
}

/* The miniature of the bars motif: five hairline columns, each a small chart with
   one bar solid. Replaces `.thumb-system`, three plates with the middle one white,
   which drew the three stages but nothing about testing. */
.thumb-bars {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  padding: 18px;
}

.thumb-bars i {
  position: relative;
  border-left: 1px solid rgba(255,255,255,.34);
}

.thumb-bars i:last-child {
  border-right: 1px solid rgba(255,255,255,.34);
}

/* three bars per column: the middle one is the winner, at --w of the height */
.thumb-bars i::after {
  position: absolute;
  /* The three bars sit on the cell's thirds -- 1/6, 3/6, 5/6 -- rather than on
     its edges. Anchored to the edges they bunched at every divider: 28px across
     a seam against 39px inside a cell, which is what made the run read as
     unevenly spaced. On the thirds the pitch is a third of a cell everywhere,
     seams included. 3.5px is half a bar. */
  right: calc(100% / 6 - 3.5px);
  bottom: 24%;
  left: calc(100% / 6 - 3.5px);
  height: 34px;
  /* The accent is floored so it always tops the two decorative bars beside it:
     --w:2 resolved to 16px and sat under its own 18px neighbour, so the
     emphasised column rendered as the shortest one in the row. */
  background:
    linear-gradient(0deg, #fff, #fff) center bottom / 7px max(22px, calc(var(--w, 3) * 8px)) no-repeat,
    linear-gradient(0deg, rgba(255,255,255,.40), rgba(255,255,255,.40)) left bottom / 7px 14px no-repeat,
    linear-gradient(0deg, rgba(255,255,255,.40), rgba(255,255,255,.40)) right bottom / 7px 18px no-repeat;
  content: "";
}

.thumb-system {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  align-items: center;
  padding: 24px;
}

.thumb-system i {
  position: relative;
  aspect-ratio: .78;
  border: 1px solid rgba(255,255,255,.42);
  background: rgba(255,255,255,.12);
}

.thumb-system i:nth-child(2) {
  background: #fff;
  transform: translateY(-8px);
}

@media (max-width: 760px) {
  .article-head {
    padding-top: 126px;
  }

  /* `.article-visual { min-height: 520px }` used to live here. It sat AFTER the
     stage's own responsive reserves and so out-ordered them below 760px, which
     silently undid the reservation and let the page shift again at 390 and 360.
     The stage's heights are declared in one place, at the top of this file. */

  .article-summary {
    grid-template-columns: 1fr;
  }

  /* the centre track is already the full width here, so the label and arrow
     stay in one row; they just need less room reserved between them */
  .article-next > div {
    padding-right: 60px;
  }

  .five-shifts-graphic {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    width: calc(100% - 36px);
    padding: 24px 0;
  }

  /* stacked, the five columns are five rows, so there is no shared track to
     subgrid into and each card is a plain block again */
  .five-shifts-graphic article {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    grid-row: auto;
    grid-template-rows: none;
    min-height: 0;
    padding: 18px 20px;
    border-top: 1px solid rgba(255,255,255,.2);
    border-right: 1px solid rgba(255,255,255,.24);
  }

  .five-shifts-graphic article > b,
  .five-shifts-graphic article > i,
  .five-shifts-graphic article > h2,
  .five-shifts-graphic article > p { grid-row: auto; }

  .five-shifts-graphic b {
    margin-bottom: 14px;
  }

  .five-shifts-graphic h2 {
    display: block;
  }

  /* The eras hero had no stacked rule at all, so at 430px its three columns
     stayed side by side at 106px each: "A single message serves the entire
     market." broke to one word per line and the third column's dot field ran
     past the right rail. It stacks the way five-shifts does, at the same
     breakpoint, so the two heroes agree. The three marks stay: one bar, then
     four, then a field IS the argument here, and a stacked row is wide enough
     to draw them properly. */
  .eras-graphic {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    width: calc(100% - 36px);
    padding: 24px 0;
  }

  .eras-head {
    grid-column: auto;
    padding: 0 20px 20px;
  }

  .eras-graphic article {
    display: block;
    grid-row: auto;
    grid-template-rows: none;
    padding: 18px 20px 22px;
    border-top: 1px solid rgba(255, 255, 255, .2);
    border-right: 1px solid rgba(255, 255, 255, .24);
  }

  .eras-graphic article > b,
  .eras-graphic article > .eras-viz,
  .eras-graphic article > .rule,
  .eras-graphic article > h2,
  .eras-graphic article > p { grid-row: auto; }

  .eras-graphic b { display: block; margin-bottom: 14px; }

  .eras-viz { height: 20px; margin: 0 0 16px; }

  .eras-graphic .rule { margin-bottom: 16px; }

  /* stacked, there is no shared baseline to hold the titles to */
  .eras-graphic h2 { display: block; }

  .five-shifts-graphic i {
    display: none;
  }

  .five-shifts-graphic p {
    display: none;
  }

  .loop-graphic,
  .system-graphic {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    gap: 10px;
    width: calc(100% - 36px);
    padding: 26px 0;
  }

  .loop-core {
    grid-column: 1;
    grid-row: auto;
    width: 180px;
  }

  .loop-node,
  .loop-node:nth-of-type(2),
  .loop-node:nth-of-type(4) {
    max-width: none;
    justify-self: stretch;
  }

}

/* ============================================================================
   Footnote apparatus
   Superscript references link to the sources list; each source carries a
   back-link to the first citation. Digits stay mono and blue, the anchor
   itself is invisible chrome (no underline).
   ========================================================================= */
/* `.launch-article` is gone: welcome-to-uplane was the only page using it and it
   now sits on `.article-body` like the other three, so the duplicated selectors
   here went with assets/welcome-article.css. */
.article-body sup a {
  color: inherit;
  text-decoration: none;
}

.article-body sup a:hover {
  text-decoration: underline;
  text-underline-offset: .2em;
}

.article-sources .fn-back {
  margin-left: 8px;
  color: var(--blue);
  font-family: var(--sans);
  font-size: 11px;
  text-decoration: none;
}

.article-sources li:target {
  color: var(--ink);
}

/* ============================================================================
   Closing CTA band
   The approved home-page closing treatment, reused beneath each article:
   squared, spanning the outer rails, artwork under a top-down gradient with
   the ken-burns breath, testimonial + glass capture form centred inside.
   Quote styles mirror managed-growth's .cta-closing (mg.css is page-scoped,
   so the rules are duplicated here for the blog routes).

   Add `closing--quiet` to drop the photograph, the way enterprise-software and
   managed-growth already close: this band and the footer hero were two
   full-bleed blue images 24px apart, so neither one landed and the quote in
   the middle of the first lost its authority. The section keeps its footprint
   and stays transparent, so the rails run straight through it.
   ========================================================================= */
.cta-closing .cta-quote {
  margin: 0;
  font-family: var(--serif);
  font-weight: 500;
  font-size: var(--fs-quote);
  line-height: 1.4;
  letter-spacing: -.012em;
  color: #fff;
}

.cta-closing .cta-who {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 0 0 28px;
}

.cta-closing .cta-who > img {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
}

/* Defensive: the portrait is the one asset in this block that can go missing,
   and a 40px broken-image icon on a photographic band is worse than a disc. A
   tinted circle underneath means an absent file degrades to a neutral mark. */
.cta-closing .cta-who > img {
  background: rgba(10, 16, 60, .10);
}

/* the client mark beside the attribution, behind a hairline so the name and the
   company do not read as one string. hero-magic-mind.webp is black (it was cut
   for light surfaces), so it takes the site's own whitening filter -- the same
   `brightness(0) invert(1)` site.css:3848 uses on the hero ticker, per §9.4's
   "recolor to white for dark". */
.cta-closing .cta-org {
  display: flex;
  align-items: center;
  flex: none;
  min-height: 46px;
  padding-left: 18px;
  margin-left: 2px;
  border-left: 1px solid rgba(255, 255, 255, .28);
}

.cta-closing .cta-org img {
  width: auto;
  height: 28px;
  max-width: 130px;
  border-radius: 0;
  background: none;
  opacity: .88;
  /* whitened for the photographic band; re-toned for the quiet variant below */
  filter: brightness(0) invert(1);
}

/* .closing--quiet is this CTA on WHITE, not on band art -- the type is --ink
   there, so the whitening filter turned a black mark into an invisible white
   one. The mark keeps its own colour and the divider flips to the light-surface
   hairline. Per-context re-toning, the §14.12 pattern. */
.cta-closing.closing--quiet .cta-org {
  border-left-color: rgba(10, 16, 60, .16);
}

.cta-closing.closing--quiet .cta-org img {
  filter: none;
  opacity: .82;
}

.cta-closing .cta-who > span:not(.cta-org) {
  text-align: left;
  font-family: var(--sans);
  /* an attribution is --fs-meta (§17.2); 13.5px was a half-pixel off the rung */
  font-size: var(--fs-meta);
  line-height: 1.4;
  color: rgba(255, 255, 255, .8);
}

/* the name is the inline bold label, the role beside it is meta -- exactly the
   pair index.html sets on `#results .tstmnl-who`. It was inheriting --fs-meta
   from the span, so name and role were the same size and only weight told them
   apart. */
.cta-closing .cta-who strong {
  display: block;
  font-size: var(--fs-label);
  font-weight: 500;
  color: #fff;
}

.cta-closing .capture { margin-top: 30px; }

/* A client wordmark drawn as a mask rather than placed as an image. The Arya
   asset is cream on transparent, cut for dark surfaces, so on this white
   variant `filter: none` would have shipped an invisible logo and the dark
   variant's `brightness(0) invert(1)` only ever produces white. Masking throws
   the file's colour away and paints the alpha in `currentColor`, which is
   already white on the band and --muted on the quiet variant, so one asset is
   correct on both without a per-surface filter. --org-ratio keeps the box on
   the file's own aspect. */
.cta-closing .cta-org-mark {
  display: block;
  height: 26px;
  width: calc(26px * var(--org-ratio, 4));
  opacity: .82;
  background-color: currentColor;
  -webkit-mask: var(--org-mark) left center / contain no-repeat;
  mask: var(--org-mark) left center / contain no-repeat;
}

@media (max-width: 640px) {
  .cta-closing .cta-who { gap: 12px; margin-bottom: 22px; }
  .cta-closing .cta-who > img { width: 68px; height: 68px; }
  .cta-closing .cta-org { padding-left: 12px; }
  .cta-closing .cta-org img { height: 22px; max-width: 96px; }
  .cta-closing .cta-org-mark { height: 18px; width: calc(18px * var(--org-ratio, 4)); }
}

/* ---- the same band without the photograph ---- */
.cta-closing.closing--quiet .closing-inner {
  max-width: 760px;
}

/* In-article, above the footnotes. The same block, but it is no longer the
   last thing on the page: a 420px floor under 96px of padding made it taller
   than any figure in the body and pushed the sources off the fold. It keeps
   the treatment and gives back the height it only needed as a closing band. */
.article-body > .article-quote {
  min-height: 0;
  margin-block: clamp(40px, 5vw, 64px);
  padding-block: clamp(48px, 5.5vw, 72px);
  border-top: 1px solid var(--art-hair);
  border-bottom: 1px solid var(--art-hair);
}

.cta-closing.closing--quiet .cta-quote {
  color: var(--ink);
}

.cta-closing.closing--quiet .cta-who strong {
  color: var(--ink);
}

.cta-closing.closing--quiet .cta-who span {
  color: var(--muted);
}

/* the quote mark hangs in the margin so the first word keeps the centre axis,
   the detail the photographic version could hide behind its own contrast */
.cta-closing.closing--quiet .cta-quote {
  text-indent: -.42em;
}

.cta-closing .closing-kb {
  animation: kenburns 40s var(--ease-in-out-quart) infinite alternate;
}

@keyframes kenburns {
  from { transform: scale(1.05); }
  to { transform: scale(1.14) translateY(-2%); }
}

@media (prefers-reduced-motion: reduce) {
  .cta-closing .closing-kb { animation: none; }
}

/* ---- D. next-best-action: competing candidate actions, one selected ---- */
.nba-row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
}

.nba-option {
  padding: 16px 14px;
  border-left: 1px solid var(--fig-line);
}

.nba-option:first-child { padding-left: 0; border-left: 0; }
.nba-option:last-child { padding-right: 0; }

.nba-option b {
  display: block;
  color: var(--muted);
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.nba-option strong {
  display: block;
  margin-top: 8px;
  color: var(--ink);
  font-size: 14.5px;
  font-weight: 500;
  letter-spacing: -.008em;
  line-height: 1.3;
}

.nba-option p {
  margin-top: 7px;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.5;
}

.nba-option.is-chosen {
  border-left: 0;
  border-radius: 10px;
  padding: 16px;
  background: var(--blue);
}

.nba-option.is-chosen b { color: rgba(255, 255, 255, .72); }
.nba-option.is-chosen strong { color: #fff; }
.nba-option.is-chosen p { color: rgba(255, 255, 255, .78); }

.nba-option.is-chosen + .nba-option { border-left: 0; }

@media (max-width: 640px) {
  .nba-row { grid-template-columns: 1fr; gap: 14px; }
  .nba-option { padding: 0; border-left: 0; }
  .nba-option.is-chosen { padding: 16px; }
}

/* ---- E. exploration vs exploitation budget split ----
   THE SITE HAS ONE CHART DEVICE AND THIS IS IT (DESIGN-BOOK §15.4): a label on
   the left, its value on the right, and a light track underneath carrying a
   proportional fill. One shape, repeated once per row. Two colours and no
   legend -- track `rgba(10,16,60,.06)`, grey fill `rgba(10,16,60,.30)`, and
   --blue for the one bar that matters. The rows are labelled, so nothing needs
   keying.

   It replaces a single 100% bar split 28 / 72 into a tinted half and a solid
   blue half. That bar was readable but it made Exploit the loud one purely
   because it was bigger, and the figure's whole point is that the small
   protected share is what buys the learning. On one shared axis the 28% bar in
   blue against a grey 72% says that without a caption doing the work.

   The axis is the budget, so the two shares sum to exactly 100%: the fills are
   real measurements of one quantity, not a graphic device. */
.fchart {
  display: grid;
  gap: 0;
}

.fchart-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 4px 16px;
  padding: 18px 0;
  border-top: 1px solid var(--fig-line);
}

.fchart-row:first-child {
  border-top: 0;
  padding-top: 4px;
}

.fchart-row b {
  color: var(--ink);
  font-size: var(--fs-body);
  font-weight: 500;
  letter-spacing: -.006em;
}

/* the value is data, so it is the display face -- the same move §15.4's head
   stat makes, which is what lets the bars be read without a tick axis */
.fchart-val {
  align-self: baseline;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.01em;
  line-height: 1;
}

.fchart-track {
  grid-column: 1 / -1;
  display: block;
  height: 8px;
  margin-top: 10px;
  background: rgba(10, 16, 60, .06);
}

.fchart-track em {
  display: block;
  width: var(--v);
  /* a 229px mobile track still has to show the smaller share (§15.4) */
  min-width: 4px;
  height: 100%;
  background: rgba(10, 16, 60, .30);
}

.fchart-track em.is-signal {
  background: var(--blue);
}

/* The track spans the whole card, because the card's width IS the axis. Prose
   must not: at 1136px the figure gives its body a 1086px measure, and 13.5px
   copy set that wide is about 190 characters a line. Capped at a readable
   measure while the bar it annotates keeps the full scale. */
.fchart-row p {
  grid-column: 1 / -1;
  max-width: 74ch;
  margin-top: 12px;
  color: var(--muted);
  font-size: var(--fs-meta);
  line-height: 1.5;
}

/* the three failure modes, as hairline rows under the chart rather than a
   bulleted list: they are readings of the same axis, not prose */
.fnotes {
  display: grid;
  gap: 0;
  margin: 24px 0 0;
  padding: 0;
  border-top: 1px solid var(--fig-line);
  list-style: none;
}

/* The index column is a fixed 108px, not `auto`. Each `li` is its own grid, so
   an `auto` track is sized per row and the three labels ("IF 0%", "IF 100%",
   "IN PRACTICE") each set their own column width -- the sentences beside them
   started at 265, 280 and 313px, three left edges where there should be one.
   108px is what the longest label needs at mono 11px / .13em. */
.fnotes li {
  display: grid;
  grid-template-columns: 108px minmax(0, 1fr);
  gap: 14px;
  align-items: baseline;
  padding: 13px 0;
  border-bottom: 1px solid var(--fig-line);
  color: var(--muted);
  font-size: var(--fs-meta);
  line-height: 1.5;
}

.fnotes li > span {
  max-width: 74ch;
}

/* Avatar, name, divider and client mark is a four-part row. On the band
   variant, whose inner column is ~308px at phone width against the quiet
   variant's 760px, it ran out of room: the name wrapped to three lines and the
   wordmark still finished hard against the container edge. Below 560 the mark
   takes its own line under the pair, and the divider that separated them turns
   from a vertical rule into the horizontal one above it. */
@media (max-width: 560px) {
  .cta-closing .cta-who { flex-wrap: wrap; row-gap: 14px; }

  .cta-closing .cta-org {
    flex-basis: 100%;
    justify-content: center;
    min-height: 0;
    margin-left: 0;
    padding: 14px 0 0;
    border-left: 0;
    border-top: 1px solid rgba(255, 255, 255, .28);
  }

  .cta-closing.closing--quiet .cta-org {
    border-top-color: rgba(10, 16, 60, .16);
  }
}

@media (max-width: 560px) {
  /* the label takes its own line rather than squeezing the sentence to nothing */
  .fnotes li {
    grid-template-columns: minmax(0, 1fr);
    gap: 6px;
  }
}

.fnotes li:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.fnotes li::before {
  display: none;
}

/* a mono index: §15.6 lists indices under the stamp device */
.fnotes b {
  color: var(--muted);
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

.fnotes strong {
  color: var(--ink);
  font-weight: 500;
}

/* ---- F. a three-step loop, as a figure rather than a blue panel ----
   Welcome stated "Analyze. Create. Optimize." inside a 1088x520 panel filled with
   a brand-blue gradient, carrying three columns of white type. Nothing on this
   site is a blue surface that size: blue is an eyebrow, a tick, an index, a 2px
   progress bar or a button. The steps are the same three columns on the figure
   kit's white card instead, divided by its hairlines, which is how the phases
   diagram on five-shifts already draws a sequence. */
.afig-loop {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
}

.afig-loop > div {
  padding: 0 20px;
  border-left: 1px solid var(--fig-line);
}

.afig-loop > div:first-child {
  padding-left: 0;
  border-left: 0;
}

.afig-loop > div:last-child {
  padding-right: 0;
}

/* a mono index: §15.6 lists indices under the stamp device */
.afig-loop b {
  display: block;
  color: var(--muted);
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

.afig-loop h4 {
  margin: 12px 0 8px;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.012em;
  line-height: 1.1;
}

.afig-loop p {
  color: var(--muted);
  font-size: var(--fs-meta);
  line-height: 1.55;
}

@media (max-width: 700px) {
  .afig-loop {
    grid-template-columns: 1fr;
    gap: 26px;
  }

  .afig-loop > div {
    padding: 0;
    border-left: 0;
  }

  .afig-loop > div + div {
    padding-top: 26px;
    border-top: 1px solid var(--fig-line);
  }
}

/* ============================================================================
   SIX FIGURES THAT DRAW THEIR ARGUMENT (DESIGN-BOOK §32)

   `Beyond the campaign` and `Production to learning` between them carried the
   SAME component, `.afig-loop`, three times: three columns of prose behind a
   card head, once per article and twice in one of them. A figure that is a list
   is not a figure -- five-shifts is the approved reference precisely because
   each of its three carries a drawing that says something the sentence beside
   it cannot (a 5-to-1-to-3 wire schematic, an assembly diagram, a phase ladder
   under an effort/trust wedge).

   Each device below is used ONCE. That is deliberate: the moment two sections
   share a diagram, the diagram has stopped describing either of them.

   Everything draws through the --fig-* tokens (§ the .afig--stage block above),
   so all six sit on the warm card and would survive being moved to white. Brand
   blue is the single accent in each and it always marks the same thing: the
   live element, the one that changed, the one that was selected.
   ========================================================================= */

/* ---- G. two clocks on one axis --------------------------------------------
   "The campaign calendar is not the customer's clock" was three columns headed
   Detect / Interpret / Respond, which is a description of any event system and
   not of the claim in the heading. Two lanes on ONE shared axis state it
   directly: the top row is on a grid, the bottom row is not, and the section's
   own worked example -- an acquisition offer landing just after a return -- is
   the place where the two lanes touch. */
.afig-clock { display: block; }

.afig-clock svg { display: block; width: 100%; height: auto; }

.clk-lane-label {
  fill: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: .13em;
  text-transform: uppercase;
}

/* The named sends and the three months. Both are data labels rather than lane
   signposts, so they drop the .13em uppercase treatment: at 10px, tracked caps
   are ~30% wider, and "MEMBERS' WEEK" centred on a bar 190 units from its
   neighbour collided with both of them. Sentence case keeps the axis readable
   and lets the callouts carry dates that match where the marks actually sit. */
.clk-bar-label,
.clk-month {
  fill: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
}

.clk-axis { stroke: var(--fig-line-2); }
.clk-tick { stroke: var(--fig-line); }

/* a scheduled send is planned material, so it takes the part tile -- the same
   cream-on-hairline the atomic diagram gives an unbuilt component. --fig-tile-2
   rather than --fig-tile: one step off the card was not enough separation for a
   12px mark standing beside an 8px solid-blue one. */
.clk-plan { fill: var(--fig-tile-2); stroke: var(--fig-line-2); }

/* a customer event is the live thing on the page, so it is the blue */
.clk-event { fill: var(--blue); }

/* the ring marks the one event the annotation names */
.clk-ring { fill: none; stroke: var(--blue); stroke-opacity: .42; }

/* the collision: a hairline dropped between the two lanes, dashed so it reads
   as an annotation rather than as a third data series */
.clk-hit {
  fill: none;
  stroke: var(--fig-line-2);
  stroke-dasharray: 2 3;
}

.clk-note {
  fill: var(--fig-ink);
  font-family: var(--sans);
  font-size: 11.5px;
}

.clk-note.is-muted { fill: var(--fig-muted); }

.afig-clock figcaption,
.clk-key {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 22px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--fig-line);
  color: var(--fig-muted);
  font-size: 12.5px;
  line-height: 1.5;
}

.clk-key span { display: flex; align-items: baseline; gap: 8px; }

.clk-key i {
  flex: none;
  width: 10px;
  height: 10px;
  border: 1px solid var(--fig-line-2);
  background: var(--fig-tile-2);
  transform: translateY(1px);
}

.clk-key i.is-event {
  border-color: var(--blue);
  background: var(--blue);
}

/* The site had no visually-hidden utility, and the matrix below needs one: its
   pass/fail column is a glyph, which carries nothing to a screen reader, and a
   <table> wants a caption that the layout has no room to show. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  font-size: var(--fs-meta);
  white-space: nowrap;
  border: 0;
}

/* ---- H. the decision layer as a matrix, not four cards --------------------
   `.nba-row` put four candidate actions side by side and coloured the winner.
   It was true but it asserted the outcome: nothing showed WHY three lost. The
   same four actions against the three tests the section names (consent,
   contact pressure, signal support) turn the figure into the readout of a
   decision rather than a picture of one. */
.afig-matrix { display: block; }

/* The selected row carries a 2px blue edge, so it needs 12px of lead-in before
   its text. Indenting only that row left a ragged first column, so every row
   takes the indent and the table is pulled back into the card's own padding by
   the same amount -- one left edge, and the blue mark sits outside it. */
.amx {
  width: calc(100% + 12px);
  margin-left: -12px;
  border-collapse: collapse;
  text-align: left;
}

.amx th:first-child,
.amx td:first-child { padding-left: 12px; }

.amx th {
  padding: 0 12px 10px 0;
  border-bottom: 1px solid var(--fig-line-2);
  color: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
  vertical-align: bottom;
}

/* The three test columns were 74px and centred, because each held one glyph.
   They now hold the value that answers the column's question, so they take real
   width and set left like every other column: a tick on its own is a verdict
   with the reason deleted. The mark floats out of the first line so a value that
   wraps stays in one block beside it rather than running under the glyph. */
.amx th.amx-test,
.amx td.amx-test {
  width: 17%;
  padding-right: 12px;
  text-align: left;
}

.amx td.amx-test .mk { float: left; margin: 2px 6px 0 0; }

.amx .amx-val {
  display: block;
  overflow: hidden;
  font-size: 12.5px;
  line-height: 1.4;
}

/* the held row: one cell across the three questions, because none of them
   apply to waiting. Three dashes there read as missing data. */
.amx td.amx-span {
  color: var(--fig-muted);
  font-size: 12.5px;
}

.amx th.amx-result,
.amx td.amx-result { width: 26%; padding-right: 0; }

.amx td {
  padding: 13px 12px 13px 0;
  border-bottom: 1px solid var(--fig-line);
  color: var(--fig-text);
  font-size: var(--fs-meta);
  line-height: 1.45;
  vertical-align: top;
}

.amx tr:last-child td { border-bottom: 0; }

.amx td.amx-action { font-weight: 500; }

/* pass is the blue tick the site already uses for a satisfied claim (`.checks
   .ck` on the home page); fail and not-applicable are hairline marks, so the
   column reads as one blue run down the selected row */
.amx .mk {
  display: inline-block;
  width: 13px;
  height: 13px;
  vertical-align: -2px;
}

.amx .mk svg { display: block; width: 100%; height: 100%; }
.amx .mk.is-pass { color: var(--blue); }
.amx .mk.is-fail,
.amx .mk.is-na { color: var(--fig-muted); }

.amx .amx-result span {
  display: block;
  color: var(--fig-muted);
  font-size: 12.5px;
  line-height: 1.4;
}

.amx .amx-result b {
  color: var(--fig-ink);
  font-size: var(--fs-meta);
  font-weight: 500;
}

/* the selected row is the one blue surface in the figure, at the tint strength
   the kit already reserves for a highlighted row rather than a solid fill --
   `.nba-option.is-chosen` was a solid --blue block, the largest blue area in
   either article */
.amx tr.is-chosen td {
  background: var(--fig-tint, rgba(26, 68, 255, .07));
  border-bottom-color: var(--fig-line-2);
}

.amx tr.is-chosen td:first-child {
  box-shadow: inset 2px 0 0 var(--blue);
}

.amx tr.is-chosen .amx-result b { color: var(--blue); }

@media (max-width: 700px) {
  /* the matrix stops being a matrix and becomes one block per action, with the
     three tests as an inline run -- a 5-column table at 320px is unreadable */
  .amx, .amx tbody, .amx tr, .amx td { display: block; width: auto; }
  .amx thead { display: none; }

  .amx tr {
    padding: 14px 0;
    border-bottom: 1px solid var(--fig-line);
  }

  .amx tr:last-child { border-bottom: 0; }
  .amx td { padding: 0; border-bottom: 0; }
  .amx td + td { margin-top: 6px; }

  /* `.amx td.amx-test` and `.amx td.amx-result` carry column widths at 0-2-1,
     which outranks this block's 0-1-1 `.amx td { width: auto }` however late it
     comes: the result column stayed pinned to 26% of a 276px card and set its
     explanation one word per line. Both are released here explicitly. */
  .amx td.amx-test,
  .amx td.amx-result { width: auto; }

  /* the tests were an inline run of three glyphs; they are sentences now, so
     each takes its own line under the action */
  .amx td.amx-test {
    display: block;
    text-align: left;
  }

  .amx td.amx-test .mk {
    float: none;
    margin: 0 6px 0 0;
    vertical-align: -2px;
  }

  .amx .amx-val { display: inline; }

  .amx td.amx-test::before {
    content: attr(data-test) " ";
    color: var(--fig-muted);
    font-family: var(--sans);
    font-size: 10px;
    letter-spacing: .13em;
    text-transform: uppercase;
  }

  .amx tr.is-chosen td:first-child { padding-left: 10px; }
  .amx tr.is-chosen td { background: none; }
  .amx tr.is-chosen { box-shadow: inset 2px 0 0 var(--blue); padding-left: 10px; }
}

/* ---- I. a persona beside a decision context -------------------------------
   The section's title is "segments of one are decision contexts, NOT personas",
   and the figure under it was three columns headed Evidence / Boundaries /
   Assembly -- the same component the same article had already used two figures
   earlier. A contrast in the heading wants a contrast in the drawing: one
   sealed block that was written once, beside one assembled from five live
   feeds and thrown away after a single decision. */
.afig-versus {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 30px;
}

.vs-col > b {
  display: block;
  margin-bottom: 4px;
  color: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

.vs-col > h4 {
  margin-bottom: 14px;
  color: var(--fig-ink);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.008em;
}

.vs-stack {
  margin: 0;
  padding: 0;
  border: 1px solid var(--fig-line-2);
  list-style: none;
}

.vs-stack li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 12px;
  border-top: 1px solid var(--fig-line);
  color: var(--fig-text);
  font-size: 12.5px;
  line-height: 1.35;
}

.vs-stack li:first-child { border-top: 0; }

/* the persona's rows are one surface: it is a single record, so nothing inside
   it is separately sourced */
.vs-static .vs-stack { background: var(--fig-tile); }

/* every row of a decision context arrives from somewhere, and the source is
   named beside it -- the 2px blue edge is the same mark `.al-prog` uses for a
   live feed, spent once per row rather than as a fill */
.vs-live .vs-stack li { box-shadow: inset 2px 0 0 var(--blue); }

.vs-stack em {
  flex: none;
  color: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
  font-style: normal;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.vs-col > p {
  margin-top: 12px;
  color: var(--fig-muted);
  font-size: 12.5px;
  line-height: 1.5;
}

.vs-col > p strong { color: var(--fig-ink); font-weight: 500; }

@media (max-width: 640px) {
  .afig-versus { grid-template-columns: 1fr; gap: 26px; }
}

/* ---- J. control beside variant, one row different -------------------------
   "A testable brief defines one question, one controlled variable, one decision
   rule" was that sentence split across three columns, which restates it rather
   than showing it. Two builds of the same asset with four rows held identical
   and ONE row different is the definition, drawn: the controlled variable is
   the only place the two columns disagree, and it is the only blue on the
   card. */
.afig-control { display: block; }

.ctl-grid {
  display: grid;
  grid-template-columns: minmax(0, 132px) minmax(0, 1fr) minmax(0, 1fr);
  gap: 0;
  align-items: stretch;
}

.ctl-grid > * { padding: 10px 14px; border-bottom: 1px solid var(--fig-line); }

.ctl-head {
  padding-bottom: 9px;
  border-bottom: 1px solid var(--fig-line-2);
  color: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: .13em;
  text-transform: uppercase;
}

.ctl-row-label {
  padding-left: 0;
  color: var(--fig-muted);
  font-size: 12.5px;
}

.ctl-cell {
  color: var(--fig-muted);
  font-size: 12.5px;
  line-height: 1.4;
}

/* a held-fixed cell says so once, in the muted voice, so the eye skips four
   rows and lands on the fifth */
.ctl-cell.is-held { background: var(--fig-tile); }

.ctl-cell.is-var {
  color: var(--fig-ink);
  font-weight: 500;
  box-shadow: inset 0 -2px 0 var(--blue);
}

/* the label of the varying row is promoted out of the muted voice too, so the
   row reads as one object across all three columns */
.ctl-row-label.is-var {
  color: var(--fig-ink);
  font-weight: 500;
}

.ctl-grid > :nth-last-child(-n + 3) { border-bottom: 0; }

/* the question and the decision rule are the two things a brief must state, so
   they sit under the build as named rows rather than as prose */
.ctl-out {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr);
  gap: 14px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--fig-line-2);
  color: var(--fig-text);
  font-size: 12.5px;
  line-height: 1.5;
}

.ctl-out + .ctl-out { margin-top: 10px; padding-top: 10px; border-top: 0; }

.ctl-out b {
  color: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

@media (max-width: 640px) {
  .ctl-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .ctl-row-label { grid-column: 1 / -1; padding: 12px 0 2px; border-bottom: 0; }
  .ctl-grid > .ctl-head:first-child { display: none; }
  .ctl-out { grid-template-columns: 1fr; gap: 4px; }
}

/* ---- K. variants circulating between two budgets --------------------------
   THE FIGURE THIS REPLACES WAS THE ONE ACTUAL DEFECT. `.fchart` is the site's
   chart device and its contract, written into section E above, is that "the
   fills are real measurements of one quantity". The markup here set BOTH rows
   to `--v:100%` and labelled them "Protected" and "Remainder": two full-width
   bars, no number, no axis, nothing measured. It had the form of a chart and
   the content of a caption.

   There is no split to plot -- the section's own point is that the allocation
   is "a governed decision, not a fixed percentage" -- so plotting one would
   have meant inventing it. What IS true and drawable is the movement: a
   challenger crosses into exploit when evidence clears the threshold, a
   decaying winner crosses back. The two lanes and the two crossings are the
   mechanism the section describes, with no quantity claimed. */
.afig-flow { display: block; }

.afig-flow svg { display: block; width: 100%; height: auto; }

/* the exploit lane is a filled band because that budget is committed; the
   explore lane is a dashed outline because it is reserved rather than spent --
   the dash is the "protected" in its label */
.flw-lane { fill: var(--fig-tile); }

.flw-lane.is-explore {
  fill: none;
  stroke: var(--fig-muted);
  stroke-opacity: .5;
  stroke-dasharray: 4 4;
}

.flw-label {
  fill: var(--fig-ink);
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
}

.flw-sub {
  fill: var(--fig-muted);
  font-family: var(--sans);
  font-size: 9.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
}

/* an untested variant is a part (cream on a hairline); a promoted one is a
   finished asset, so it takes the built tile plus the single blue cap -- the
   same two states the atomic diagram already defines, reused here because it is
   literally the same object at two stages of its life */
.flw-part { fill: var(--fig-tile); stroke: var(--fig-line-2); }
.flw-built { fill: var(--fig-surface); stroke: var(--fig-line); }
.flw-cap { fill: var(--blue); }

.flw-arrow { fill: none; stroke: var(--blue); stroke-width: 1.25; }
.flw-arrow.is-back { stroke: var(--fig-line-2); }
.flw-head { fill: var(--blue); }
.flw-head.is-back { fill: var(--fig-line-2); }

.flw-move {
  fill: var(--fig-text);
  font-family: var(--sans);
  font-size: 11px;
}

/* ---- L. who hands what to whom --------------------------------------------
   The hybrid-workflow figure used `.afig-phases`, which is five-shifts'
   adoption ladder -- the same four-row component, in an article that links to
   five-shifts two screens later. It also dropped `.phase-scale`, the wedge that
   is the only drawn thing in the original, so what remained was the list with
   the graphic taken out.

   The section's own claim is that "the system makes every handoff visible,
   reviewable, and reversible", so the handoffs are the subject: two lanes, and
   a marked node every time work crosses between them. */
.afig-lanes { display: block; }

.afig-lanes svg { display: block; width: 100%; height: auto; }

.lns-lane { stroke: var(--fig-line); }

.lns-tag {
  fill: var(--fig-muted);
  font-family: var(--sans);
  font-size: 9.5px;
  letter-spacing: .13em;
  text-transform: uppercase;
}

/* a human step is an outline, an agent step is a filled tile: the lane a node
   sits in already says who owns it, so the fill carries the second fact --
   whether a person is in the loop at that step */
.lns-human { fill: var(--fig-surface); stroke: var(--blue); }
.lns-agent { fill: var(--fig-tile); stroke: var(--fig-line-2); }

.lns-node-t {
  fill: var(--fig-ink);
  font-family: var(--sans);
  font-size: 11.5px;
  font-weight: 500;
}

.lns-wire { fill: none; stroke: var(--fig-line-2); }

/* a handoff is the one thing this figure is about, so a crossing wire is blue
   and carries the review dot; a wire that stays inside a lane is a hairline */
.lns-wire.is-cross { stroke: var(--blue); }
.lns-dot { fill: var(--fig-surface); stroke: var(--blue); }

/* the key sits under the drawing on the same hairline the clock figure's key
   uses, and its bullet IS the review dot at text scale */
.lns-key {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--fig-line);
  color: var(--fig-muted);
  font-size: 12.5px;
  line-height: 1.5;
}

.lns-key > span {
  flex: none;
  width: 9px;
  height: 9px;
  border: 1.5px solid var(--blue);
  border-radius: 50%;
  background: var(--fig-surface);
  transform: translateY(1px);
}

@media (prefers-reduced-motion: no-preference) {
  /* the fill draws from its own left edge, the one motion this figure has */
  .article-figure.in .fchart-track em {
    animation: article-line 1.5s var(--ease-out-quint) both;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .five-shifts-graphic i {
    animation: article-line 2.8s var(--ease-out-quint) both;
  }

  /* `.score-bars i::after` and `.variant-stack i:nth-child(2)` were the other
     two animations here. Both belonged to `.system-graphic`, which is gone: the
     hero stage now carries one agent-log card and the card sequencer owns its
     motion (§7.2). `article-winner` went with them -- nothing else used it. */
  @keyframes article-line {
    from { transform: scaleX(0); transform-origin: left; }
    to { transform: scaleX(1); transform-origin: left; }
  }
}


/* ============================================================================
   TWO FIGURES FOR `welcome-to-uplane` (DESIGN-BOOK §32 applied to the fourth
   article)

   §32 rebuilt the in-body figures on the two sibling articles and left welcome
   alone. Welcome was carrying the same defect §32.1 names: a plain-white
   `.afig` (no band-art stage, so it read as a diagram pasted into a document
   rather than as one of the site's product graphics), and `.afig-loop` -- a
   card head over three columns of prose, which is a list, not a figure.

   Both are on `.afig--stage` now and each draws something its caption cannot
   say. Devices already spoken for: `-orch`, `-atomic`, `-phases` (five-shifts),
   `-clock`, `-matrix`, `-versus` (beyond the campaign), `-control`, `-flow`,
   `-lanes` (production to learning). These two are new, and §32.2 still holds:
   a figure is used once.

   `.afig-loop` becomes unused, the way `.fchart` was left unused in §32.3 --
   the component is not wrong, it was being asked to be a drawing.

   Blue keeps its one job in both: it marks the live element.
   ========================================================================= */

/* ---- M. one signal carried through four checks ----------------------------
   The four jobs are the spine of "Beyond the Campaign" and they had no drawing:
   four <h3>s between two figures about softer claims, which is why the hero card
   promising an architecture read as filler. Four boxes and three arrows would
   only restate the headings, so each step shows what it READ and what it PASSED
   ON, with the numbers from the one account the article follows. The blue is
   spent once, on the event, because that is the live element (§ the device
   preamble above): every box after it is a constraint, not a signal. */
.afig-chain { display: block; }

.chn-signal {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 11px 14px;
  border: 1px solid var(--fig-line-2);
  background: var(--fig-tint);
  box-shadow: inset 2px 0 0 var(--blue);
}

.chn-signal b {
  flex: none;
  color: var(--blue);
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

.chn-signal span {
  color: var(--fig-ink);
  font-size: 12.5px;
  line-height: 1.4;
}

.chn-steps {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  margin: 22px 0 0;
  padding: 0;
  list-style: none;
}

.chn-steps li {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 12px 13px 13px;
  border: 1px solid var(--fig-line);
  background: var(--fig-surface);
}

/* The steps are an <ol> inside .article-body, and `.article-body li::before`
   paints the brand's blue tick on every list item there as a masked 17px box.
   It has to be unset explicitly, the same trap `.article-sources li::before`
   documents above: leaving it on put a blue tick in each gap between the
   boxes, which read as a pass mark on a figure that scores nothing. */
.chn-steps li::before {
  display: none;
  content: none;
}

/* The link between two boxes, drawn in the gap rather than set as a glyph in
   the markup: the sequence is the claim, so it belongs to the drawing. A wire,
   not a chevron -- a 6px arrowhead in hairline at this scale reads as a stray
   character, and --fig-line-2 is already the token this kit uses for the wires
   that carry an argument between two parts. */
.chn-steps li + li::after {
  content: "";
  position: absolute;
  top: 50%;
  left: -14px;
  width: 14px;
  height: 0;
  border-top: 1px solid var(--fig-line-2);
}

.chn-steps b {
  display: block;
  margin-bottom: 9px;
  color: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

.chn-read {
  display: block;
  color: var(--fig-text);
  font-size: 12.5px;
  line-height: 1.4;
}

/* margin-top:auto, so the four rules line up across boxes whose read-lines run
   to different lengths. Without it the figure had four dividers at four
   heights and read as ragged rather than as one sequence. */
.chn-out {
  display: block;
  /* auto pins it to the bottom of a stretched box; the min-height makes the
     four rules land on the same line as each other, since two of the four
     outcomes take two lines at this column width and two take one */
  margin-top: auto;
  /* border-box, so the padding has to be inside the figure: 2.8em is the two
     lines, the 9px is the rule's clearance above them */
  min-height: calc(2.8em + 11px);
  padding-top: 9px;
  border-top: 1px solid var(--fig-line);
  color: var(--fig-muted);
  font-size: 11.5px;
  line-height: 1.4;
}

@media (max-width: 860px) {
  .chn-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .chn-steps li + li::after { display: none; }
}

@media (max-width: 560px) {
  .chn-steps { grid-template-columns: 1fr; gap: 10px; }
  .chn-signal { display: block; }
  .chn-signal b { margin-bottom: 6px; }
  /* one column, so there is no row of rules to line up: the reserved second
     line would just be slack under the shorter outcomes */
  .chn-out { min-height: 0; }
}

/* ---- H. the creative bottleneck, drawn as a throat ------------------------
   The section's claim is a RATIO -- precisely defined audiences going in,
   generic creative coming out -- and three named constrictions between them.
   The three sentences were already correct as `.fnotes` rows; what they had no
   way of saying is how much narrower the far end is. The drawing states that
   and the rows key to its three gates by index. */
.afig-gate { display: block; }

.gate-svg {
  display: block;
  width: 100%;
  height: auto;
}

/* The audiences that need tailored creative. Not a --fig-tile: a tile is two
   steps off the card and this field IS the measurement -- at tile weight the
   left half of the drawing read as a ghost. This is the tile tone taken to the
   weight a hairline carries, so the field is legible without out-shouting the
   one blue mark. */
.gate-svg .gt-demand { fill: rgba(47, 44, 43, .20); }

/* what actually ships. Blue, because it is the live element: the only creative
   the pipeline ever produced. */
.gate-svg .gt-out { fill: var(--blue); }

/* the constriction itself is a hairline, not a filled funnel -- a solid wedge
   would out-weigh both fields and the fields are the measurement */
.gate-svg .gt-throat {
  fill: var(--fig-tile);
  fill-opacity: .5;
  stroke: var(--fig-line-2);
  stroke-width: 1;
}

.gate-svg .gt-gate {
  stroke: var(--blue);
  stroke-width: 2.5;
  stroke-linecap: butt;
}

/* THE THREE GATE INDICES, above the drawing and pinned to it.
   Same move as `.gate-ends` and the same reason: inside the SVG a 10px index
   scaled to 5.1px on a phone card and 15.8px on a desktop one. Each span is
   absolutely placed at its gate's own x as a percentage of the 480-unit canvas
   (170, 245 and 320 of 480), centred on it, so it tracks the line it names at
   every width while setting at one size. */
.gate-gates {
  position: relative;
  height: 1.2em;
  margin: 0 0 4px;
  color: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: .13em;
  line-height: 1.2;
}

.gate-gates span {
  position: absolute;
  left: var(--x);
  transform: translateX(-50%);
}

/* THE TWO ENDS OF THE RATIO, as type rather than as marks in the drawing.
   They were `<text>` inside the SVG, which means they scaled with it: the same
   `font-size: 10px` rendered 15.8px on a 760px desktop card and 5.7px on a
   246px phone one. The rule asks for 10px, so they are a row of real type
   under the drawing now, sat on the drawing's own edges. It takes the same
   inset the legend under it takes, so the drawing, the two ends and the three
   notes share one left edge and one right edge. */
.gate-ends {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  width: 97.5%;
  margin: 10px auto 0;
  color: var(--fig-muted);
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: .13em;
  line-height: 1.2;
  text-transform: uppercase;
}

/* Each end takes HALF the row and sets toward its own edge, rather than both
   being intrinsically sized. At 360px the pair wanted 246px of a 220px row, so
   the right-hand label wrapped under the left-hand one and ended up in the
   middle of the drawing. Half a row each means a narrow phone breaks each
   label over two lines at its own end, which is where the label belongs. */
.gate-ends span { flex: 1 1 0; }
.gate-ends span:last-child { text-align: right; }

/* the rows sit under the drawing and take the same top hairline the chart rows
   take, so the two halves of the figure read as one object */
.afig-gate .fnotes {
  margin-top: clamp(18px, 2vw, 26px);
  /* The drawing is inset 6 units inside its own 480-unit viewBox, so its field
     and its two captions start 1.25% in from the card's text edge. The rows ran
     to the full width, which left the index and the hairlines 9px outside every
     mark above them. Matching the inset puts the index on the field's left edge
     and the hairline ends under "CREATIVE SHIPPED". */
  width: 97.5%;
  margin-inline: auto;
}

/* The shared `.fnotes` index column is 108px, a width cut for the labels
   "IF 0%" / "IF 100%" / "IN PRACTICE" in another figure. Here the index is two
   characters, so 108px opened a ~90px hole between "01" and its sentence and
   the three sentences started nowhere in particular -- not on the drawing's
   left edge, not under its first gate. 34px is what "01" needs at mono 11px /
   .13em, which puts the index on the drawing's own left edge and the sentences
   on one edge just inside it, the same rhythm the caption row keeps. */
.afig-gate .fnotes li {
  grid-template-columns: 34px minmax(0, 1fr);
  gap: 20px;
}

/* ---- I. a patchwork against a circuit -------------------------------------
   Section 02's argument is not "AI is good", it is that the AI era is defined
   by the LOOP rather than the task: four tools each made one step faster and
   none of them closed. Two panels on one card say it without a caption -- the
   left chain is broken between every pair, the right one returns. Same four
   functions in both, same order, so the only variable is the connection. */
.afig-mesh {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(18px, 2.4vw, 30px);
}

.afig-mesh > div { min-width: 0; }

.afig-mesh > div + div {
  padding-left: clamp(18px, 2.4vw, 30px);
  border-left: 1px solid var(--fig-line);
}

.afig-mesh h4 {
  margin-bottom: 4px;
  color: var(--fig-ink);
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -.012em;
  line-height: 1.1;
}

.afig-mesh > div > b {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 10px;
  color: var(--fig-muted);
  font-family: var(--sans);
  font-size: var(--fs-stamp);
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
}

/* THE MARK ON THE UPLANE PANEL.
   Reviewed as "Uplane logo" on the right-hand panel's label. The two labels are
   a pair -- "Today" is a state of the world, "Uplane" is a named system -- so
   only one of them has a logo to carry, and carrying it is what makes the
   comparison legible before either drawing is read.

   It is the glyph `.orch-engine` already sets on five-shifts, at the label's own
   cap height and in brand blue, which is the one place §7 spends blue on a mark
   rather than on a measurement. The word stays: it is the label, and the mark is
   not a substitute for it. */
.afig-mesh > div > b .mesh-mark {
  flex: none;
  /* The glyph's canvas is 1389.5 x 1985.28, so it is 0.7 as wide as it is tall.
     Sized as a square it letterboxes to 7.8px of visible mark, which is a smudge
     rather than a logo; the height leads and the width follows the ratio. 1.15em
     is a touch over the label's cap height and still inside its 14.7px line, so
     the row does not grow. */
  width: .805em;
  height: 1.15em;
  color: var(--blue);
}

.afig-mesh p {
  margin-top: 14px;
  color: var(--fig-muted);
  font-size: var(--fs-meta);
  line-height: 1.55;
}

.mesh-svg {
  display: block;
  width: 100%;
  height: auto;
  margin-top: 16px;
}

.mesh-svg .ms-node {
  fill: var(--fig-tile);
  stroke: var(--fig-line-2);
  stroke-width: 1;
}

.mesh-svg .ms-node.is-live {
  fill: var(--fig-surface);
  stroke: var(--blue);
}

.mesh-svg .ms-label {
  fill: var(--fig-muted);
  font-family: var(--sans);
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
}

/* the broken chain: hairline stubs that stop short of the next node, with the
   gap left visible. Nothing crosses it. */
.mesh-svg .ms-stub {
  fill: none;
  stroke: var(--fig-line-2);
  stroke-width: 1.25;
  stroke-dasharray: 3 4;
}

/* the terminator bar. Without it a dashed stub reads as a weak connection to
   something off-frame; with it the wire visibly ENDS, which is the claim. */
.mesh-svg .ms-end {
  fill: none;
  stroke: var(--fig-line-2);
  stroke-width: 1.25;
}

/* the closed circuit: one continuous blue path through all four and back */
.mesh-svg .ms-wire {
  fill: none;
  stroke: var(--blue);
  stroke-width: 1.5;
}

.mesh-svg .ms-head { fill: var(--blue); }

@media (max-width: 700px) {
  .afig-mesh { grid-template-columns: 1fr; }

  .afig-mesh > div + div {
    padding-left: 0;
    padding-top: clamp(18px, 5vw, 26px);
    border-left: 0;
    border-top: 1px solid var(--fig-line);
  }
}


/* ==========================================================================
   ARTICLES: the phone pass.

   Measured at 390px before this block, the four articles ran 11,004 / 12,055 /
   14,482 / 11,821px. The prose is the article and stays; the cost was in the
   furniture around it, all of it still carrying desktop proportions once the
   two-column layouts stacked:

     hero motif      672-828px   five rows of 32/22/34 padding
     figure stage    342-1007px  52px of sky over a card with 22px of its own
     summary grid    717-863px   four cells at 30px padding
     kickers         62px top margin, five or six per article
     pull stats      54px margin + 30px padding, for two lines

   Nothing is removed and no graphic is redrawn: the cards keep every row they
   carry. They give up the air that was sized for a 1400px page. Same approach
   as the enterprise-software pass.
   ========================================================================== */
@media (max-width: 640px) {
  /* ---- the hero motif ---- */
  .article-visual {
    min-height: 0;
    padding: 22px 16px 34px;
  }

  .hm-grid article,
  .eras-graphic article {
    padding: 18px 16px 20px;
  }

  .hm-grid h2,
  .eras-graphic h2 {
    font-size: var(--fs-h3);
  }

  .hm-grid p,
  .eras-graphic p {
    margin-top: 8px;
  }

  /* ---- the figure stage ----
     52px of sky above the card was reserving room for the stamp, which is
     absolutely positioned and only needs its own line. */
  .afig--stage {
    padding: 34px 12px 16px;
  }

  .afig--stage .afig-card {
    padding: 16px;
  }

  .afig-stamp {
    top: 13px;
    left: 14px;
  }

  .afig-card-head {
    margin-bottom: 14px;
    padding-bottom: 12px;
  }

  .afig-body {
    padding: 0;
  }

  /* ---- the summary grid ---- */
  .article-summary > div {
    padding: 16px 16px 18px;
  }

  .article-summary b {
    margin-bottom: 8px;
  }

  .article-summary strong {
    font-size: var(--fs-card-ttl);
  }

  .article-summary p {
    margin-top: 7px;
    font-size: var(--fs-meta);
  }

  /* ---- the section rhythm ---- */
  .kicker {
    margin-top: 38px;
  }

  .article-body h2 {
    margin-top: 10px;
    margin-bottom: 14px;
  }

  .article-body h3 {
    margin-top: 26px;
  }

  .pull-stat {
    margin: 34px 0;
    padding: 20px 0;
  }

  .article-sources {
    margin-top: 44px;
    padding-top: 26px;
  }

  .article-body > .article-next {
    margin-top: 44px;
  }
}


/* ==========================================================================
   ARTICLES: the phone pass, round two -- every graphic inside half a screen

   Reviewed on a phone, on the hero motif and on the figure cards, as "make this
   way more concise so that I can view it on mobile. Feel free to redesign if
   needed but this needs to be on mobile and should maximum take half a mobile
   screen", and separately as "make sure across all of these graphics that the
   title is shown. If it doesn't work within them in the boxes, please move it
   outside the boxes and have it in blue eyebrows".

   Measured at 390x844 before this block, against that half-screen budget of
   422px:

     welcome      hero 782  gate 388  mesh 557  summary 495
     five-shifts  hero 522  orch 610  atomic 338  phases 569  summary 444
     engagement   hero 484  clock 269  chain 642  matrix 715  versus 920
     testing      hero 466  control 653  flow 369  lanes 257  summary 495

   Nothing is deleted and no drawing is redrawn. Round one (the block above)
   stacked the desktop columns and paid for it in height: a two-column
   comparison became a 557px scroll, a four-step sequence a 569px one, and the
   five-column hero a 782px one. This round gives the phone the pattern the rest
   of the site already uses for a row that would stack into a 600-900px column
   -- `data-m-swipe` plus the `.m-dots` rail (assets/site.css, built by
   assets/site.js), reviewed there as "please use the dot system across all
   mobile designs". A multi-part graphic shows ONE part per screen and the dots
   say how many there are.

   It also returns content the phone was throwing away: `.hm-grid.is-5` hid
   cards three, four and five below 620px, so two of the five hero points on
   engagement and on testing existed only on a desktop.
   ========================================================================= */
@media (max-width: 640px) {

  /* ---- the shared track ---------------------------------------------------
     assets/site.css sizes `[data-m-swipe]` for a card row on the PAGE: a 24px
     rail inset either side so the first card starts on the text rail, 64px of
     clearance for the card shadow, and a hairline between neighbours that a
     stacked column would have collapsed. Inside a stage or a warm-paper card
     none of that applies -- the box sets its own inset, there is no shadow to
     clear, and the drawing draws its own divisions. The scroll mechanics, the
     88% peek and the trailing spacer are left exactly as they are, so this is
     the same component and not a second one. */
  .g-track[data-m-swipe] {
    gap: 12px;
    padding: 0;
    margin-bottom: 0;
    scroll-padding-left: 0;
    /* `justify-content: center` is the one declaration a graphic cannot keep
       when it becomes a scroller. The orchestration figure centres its five
       tracks on a desktop, and a centred flex container puts its overflow on
       BOTH sides: measured here, the signals panel sat at -278px inside the
       track and no amount of scrolling could reach it, so the figure opened on
       the engine and its first third did not exist on a phone. Overflow has to
       be on one side, and the side has to be the end. */
    justify-content: flex-start;
  }

  .g-track[data-m-swipe] > * + * {
    border-top: 0;
    border-left: 0;
  }

  /* ONE PANEL PER SCREEN, AT THE FULL WIDTH OF THE BOX -- not the page's 88%
     peek plus a 12% trailing spacer.

     Two reasons, one of each kind. Inside a card there is no width to spare: at
     88% of a 274px card a panel is 240px, and that is where the two-record
     figure was setting a 12px value on two lines and its five-row list on ten.
     At full width a `.vs-stack` row keeps its desktop construction on a phone
     -- value left, source right, one line.

     And the peek does not LAND. The trailing spacer is what lets the last card
     reach the same rail as the first, and 12% of the track is not enough once
     the track carries its own rail inset: measured on the five-shift hero,
     cards one to four rested at x=20 and the fifth at x=42, and no spacer value
     moved it (the scroller settles on a snap position of 1047 whatever range it
     is given). At full width the arithmetic is exact rather than approximate --
     panel = track - 2*inset, pitch = panel + gap, and the last snap position is
     exactly the maximum scroll -- so every panel rests on the same line, which
     is what "make sure that these are all in the same position, they start and
     end at the same height" asks for.

     The dots carry the whole affordance, which is the job they were built for. */
  .g-track[data-m-swipe] > * { flex: 0 0 100%; }
  .g-track[data-m-swipe]::after { content: none; }

  /* The rail belongs to the graphic it sits in, so it takes that graphic's own
     clearance rather than the page's 4px/20px. */
  .article-visual .m-dots,
  .afig-card .m-dots {
    margin: 14px auto 0;
  }

  /* the summary's track already hands back 24px of clearance under its cards
     (64px of shadow room less the 40px negative margin), so the rail only owes
     the difference */
  .article-summary + .m-dots { margin: 0 auto 0; }

  /* On artwork the dots invert: rgba(10,10,10,.16) on navy is nothing at all.
     Same one-layer construction, same 7px pitch, white instead of ink. */
  .article-visual .m-dots button { background: rgba(255, 255, 255, .32); }
  .article-visual .m-dots::after { background: #fff; }

  /* ---- the hero motif: one column per screen ------------------------------
     The stage stops padding its sides so the track can run to the glass, and
     the track pads them back -- which is how the card rows on the page do it,
     and it is what makes the peek at the right edge visible. */
  .article-visual {
    padding: 20px 0 24px;
  }

  /* The stacked treatments at 760px sized these bands as a column inside the
     stage -- `width: calc(100% - 36px)` plus 24px of their own vertical
     padding. The track runs edge to edge instead and carries the inset itself,
     so the peek at the right edge is a peek and not a 36px gutter. */
  .hm-grid[data-m-swipe],
  .five-shifts-graphic[data-m-swipe],
  .eras-cols[data-m-swipe] {
    width: 100%;
    padding: 0 20px;
    scroll-padding-left: 20px;
  }

  /* THE RULE AND THE SUB-LINE COME BACK.
     Below 760px `.five-shifts-graphic` dropped `i` and `p` from all five
     cards, which left five bare headlines: stacked, each card had one column's
     width and five of them made a 700px band, so the only way to hold the
     height was to delete the copy. A card on the track has the full screen and
     only one of them is on it, so the hero states its five shifts in full on a
     phone for the first time. */
  .five-shifts-graphic i,
  .five-shifts-graphic p { display: block; }

  .five-shifts-graphic b,
  .eras-graphic b { display: block; margin-bottom: 0; }

  /* Subgrid is what puts the kicker, the mark, the rule and the title of five
     columns on five shared lines, and the reserved `1fr` slack row is what
     bottom-aligns them. There is one column on screen at a time now, so there
     is nothing to align across and that slack was pure height: each card sets
     from the top of its own box. The vertical hairlines go with it -- they
     divided columns that no longer sit side by side. */
  .hm-grid > article,
  .five-shifts-graphic article,
  .eras-graphic article {
    display: block;
    padding: 0 0 2px;
    border: 0;
  }

  .hm-mark,
  .eras-viz,
  .five-shifts-graphic i {
    margin: 12px 0;
  }

  .hm-mark { height: 26px; }
  .eras-viz { height: 20px; }
  .five-shifts-graphic i { margin-top: 14px; }

  .hm-grid .rule,
  .eras-graphic .rule { margin-bottom: 12px; }

  .hm-grid h2,
  .eras-graphic h2,
  .five-shifts-graphic h2 { display: block; }

  .five-shifts-graphic h2 { font-size: var(--fs-h3); }
  .five-shifts-graphic p { margin-top: 8px; }

  /* the motif's own header stays put above the track: it names the comparison,
     so it is not one of the things being compared */
  .eras-graphic { display: block; width: 100%; padding: 0; }
  .eras-head { grid-column: auto; padding: 0 20px 16px; }
  .eras-head h2 { font-size: var(--fs-h3); }
  .eras-stamp { margin-bottom: 10px; }

  /* ---- the figure's title, outside the box -------------------------------
     The stamp was a mono label at rgba(255,255,255,.72) on the stage artwork
     above the card. On a phone the stage crops to whatever part of the band
     falls behind it and the card fills nearly all of it, so the label had a
     13px strip of photograph to survive on and was the one thing in the figure
     you could not read. It leaves the box. The site's own in-panel signpost is
     the blue eyebrow (`.afig-eyebrow`, §15.6) and this is that object, moved
     from inside the artwork to above it, on the same 760px measure the
     figcaption uses -- so the figure now opens and closes on the text column.
     Above the breakpoint it is still the white stamp on the artwork. */
  .article-figure > .afig-stamp {
    position: static;
    width: min(760px, 100%);
    margin: 0 auto 12px;
    color: var(--blue);
    font-size: var(--fs-eyebrow);
    letter-spacing: .14em;
  }

  /* nothing sits on the artwork above the card any more, so the stage stops
     reserving 34px of sky for it */
  .afig--stage {
    /* The stage is a one-track grid with the card centred in it, and an auto
       track is sized from its item's MAX-CONTENT. A swipe track's max-content
       is the whole row laid out flat, so the card resolved to 379-432px inside
       a 352px stage and `overflow: hidden` cut it off -- the figure lost its
       right edge and the last panel could not be reached. An explicit
       minmax(0, 1fr) track is what stops the item's content from sizing it. */
    grid-template-columns: minmax(0, 1fr);
    padding: 14px 12px 16px;
  }

  .afig--stage .afig-card {
    padding: 14px;
  }

  /* ---- welcome: the two mesh panels ---------------------------------------
     A portfolio of point solutions beside one continuous loop. Stacked, the
     comparison was a 557px scroll in which the two halves were never on screen
     together anyway; side by side on the track they are one swipe apart. */
  .afig-mesh[data-m-swipe] > div + div {
    padding-top: 0;
    border-top: 0;
  }

  .afig-mesh h4 { font-size: var(--fs-card-ttl); }
  .afig-mesh p { margin-top: 10px; }
  .mesh-svg { margin-top: 12px; }

  /* ---- the three horizontal drawings scroll at their drawn scale ---------
     Reviewed on the gate as "this one is weird, please make it viewable into
     one view", and it was: the gate is a single centred composition whose
     whole point is the RATIO between a wide field of audiences on the left and
     one small mark on the right, so a scroller opened on the field with the
     mark off screen. It fits in one view now -- see `.gt-idx` and
     `.gate-ends` below.

     The three that keep the scroller are horizontal OBJECTS rather than
     compositions: a three-month timeline, two budget lanes, a six-step
     workflow across two swim lanes. Their axis is left-to-right, they carry 6
     to 20 callouts pinned to positions along it, and moving sideways through
     one reads as following that axis rather than as hunting for a missing
     half. Fitting them would need 2.4x on every label, which collides.
     The clipped right edge IS the affordance, the same reasoning as the 88%
     peek on a swipe row: a drawing that visibly continues past the card edge
     reads as a drawing you can move. The scrollbar is hidden because the site
     hides it on every other track it owns. */
  .afig-scroll {
    display: block;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }

  .afig-scroll::-webkit-scrollbar { display: none; }

  /* 1:1 with the 660-unit canvas all three were laid out on, so a 10px label
     is 10px */
  .afig-scroll > svg {
    width: 660px;
    max-width: none;
  }

  /* ---- the indexed legends (the gate, the budget flow) ------------------
     Three sentences, each keyed by index to a mark in the drawing above them.
     Stacked they run 220-240px, and with the drawing now taking its own 190px
     the two figures came to 451px and 485px. One sentence per screen: the
     index stays on the note, so the key still reads, and the dots say there
     are three of them. */
  /* THE GATE, IN ONE VIEW.
     No type is left inside the drawing at all, so the scale it renders at is
     free: the field is a texture, the throat is a shape, and the one blue mark
     at the narrow end is the argument. All three read at 0.57. */
  .gate-ends { margin-top: 8px; }

  .fnotes[data-m-swipe] { margin-top: 14px; }

  .fnotes[data-m-swipe] > li {
    grid-template-columns: 26px minmax(0, 1fr);
    gap: 12px;
    padding-top: 0;
    padding-bottom: 0;
  }

  /* ---- five-shifts: signals -> engine -> decisions ------------------------
     Three panels, and the two connector columns are already dropped at 820px
     because a wire between two stacked blocks lands on nothing. site.js skips
     hidden children when it counts the dots, so the rail reads three. */
  .afig-orch .orch-signals { grid-template-rows: repeat(5, 42px); }
  .orch-signals li { font-size: 13px; gap: 10px; }
  .orch-engine { min-height: 0; padding: 14px 12px; }

  /* ---- five-shifts: the four phases of adoption --------------------------
     A progression reads down a list, which is why it is rows rather than
     columns -- but four rows of three lines each plus the wedge was 569px. One
     row per screen keeps the reading order (the dots are the progression) and
     the wedge stays under the track, because it measures all four at once. */
  .afig-phases[data-m-swipe] > .phase {
    padding: 0;
    border-top: 0;
  }

  .phase-scale { margin-top: 4px; padding-top: 14px; }
  .phase-scale svg { height: 62px; }
  .phase-scale .ps-a { top: 25px; }

  /* ---- engagement: the four candidate actions ---------------------------
     Round one turned a five-column table into four stacked blocks, which is
     readable and 715px tall. The blocks are the same, one per screen: an
     action, the three tests it is scored against, and the verdict, with the
     chosen row still carrying its 2px blue edge. */
  /* The subject line names WHOSE data the drawing is and WHEN. It is one
     sentence over a card that is now 274px wide, so it takes the meta size the
     figcaption under the card takes rather than body scale. */
  .afig-subject { font-size: 11.5px; }

  .amx { margin-left: 0; }
  .amx td:first-child { padding-left: 0; }
  .amx tbody[data-m-swipe] > tr { padding: 0; border-bottom: 0; }
  .afig-subject { margin-bottom: 12px; }

  /* ---- engagement: the customer-time chart ------------------------------
     The drawing has its own width now (see `.afig-scroll`), so what is left to
     find is in the two text blocks around it: at 360px the subject line and the
     legend each took a third line and the card came to 399px. */
  .clk-key {
    gap: 4px 16px;
    margin-top: 12px;
    padding-top: 10px;
    font-size: 11.5px;
    line-height: 1.4;
  }

  /* ---- engagement: the four links of the chain --------------------------- */
  .chn-signal { padding: 9px 12px; }
  .chn-steps[data-m-swipe] { margin-top: 16px; }
  .chn-steps li { padding: 11px 12px; }

  /* ---- engagement: the two records --------------------------------------
     The persona file beside the record that was true at 09:14. It was the
     tallest graphic in the four articles at 920px, because the contrast is
     five rows against five rows and stacking it puts fifteen lines between the
     two things being contrasted. On the track they are adjacent, and the rows
     give up the space a 12px value and a right-aligned source label were
     fighting over at 240px: the source becomes the row's own small caption,
     above the value, which is how `.orch-out` sets a label over its line. */
  /* `.afig-versus` keeps a 26px grid gap for its two columns, and the subject
     line and the dot rail are rows of that same grid: the figure was paying
     52px for two gaps it no longer has anything to put between. */
  .afig-versus { gap: 0; }

  .vs-col > b { margin-bottom: 2px; }
  .vs-col > h4 { margin-bottom: 8px; font-size: 12.5px; }

  .vs-stack li {
    gap: 8px;
    padding: 4px 9px;
    font-size: 11.5px;
    line-height: 1.25;
  }

  .vs-stack em { font-size: 9.5px; letter-spacing: .08em; }

  /* The closing note on each record, at the smallest running size in the kit
     (`.chn-out` is 11.5px and this is one step under it). It is the figure's
     footnote rather than its argument -- what the record CANNOT do -- and it
     is the last thing between the two records and the half-screen budget. */
  .vs-col > p { margin-top: 8px; font-size: 11px; line-height: 1.36; }

  /* ---- testing: the two builds ------------------------------------------
     Round one dropped the row-label column and let each label span the two
     builds, which turned a five-row matrix into fifteen stacked cells. The
     matrix stays a matrix -- three columns, four rows held and one varied, so
     the blue underline is still the only place the two builds disagree -- and
     the brief's question and decision rule swipe in behind it as one panel. */
  /* `display: contents` is what keeps the two notes as direct rows of the
     figure above the breakpoint. On the track they are ONE panel, so the box
     has to exist again -- a contents box is not a flex item, and site.js reads
     it as no slide at all. */
  .ctl-notes { display: block; }

  .ctl-grid {
    grid-template-columns: minmax(0, 62px) minmax(0, 1fr) minmax(0, 1fr);
  }

  .ctl-grid > * { padding: 7px 6px; }
  .ctl-grid > .ctl-head:first-child { display: block; }
  .ctl-head { font-size: 10px; letter-spacing: .1em; }

  .ctl-row-label {
    grid-column: auto;
    padding: 7px 6px 7px 0;
    border-bottom: 1px solid var(--fig-line);
    font-size: 10.5px;
    line-height: 1.25;
  }

  .ctl-cell { font-size: 10.5px; line-height: 1.25; }

  .afig-control[data-m-swipe] .ctl-out {
    grid-template-columns: 1fr;
    gap: 4px;
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }

  .afig-control[data-m-swipe] .ctl-out + .ctl-out { margin-top: 12px; }

  /* ---- the closing summary ----------------------------------------------
     Four cells, 2x2 on a desktop and a 444-597px column on a phone. One per
     screen instead. The container's frame and lift belong to a grid that spans
     the rails; a single cell on the track is its own object, so it takes the
     site's card anatomy directly: white, a --line hairline, and the lift.

     THE LIFT NEEDED SOMEWHERE TO FALL. Reviewed as "give these buttons shadows
     typical for the uplane style" -- a first pass dropped it, because a scroller
     clips on both axes and the shadow came out sliced flat, which on a white
     section reads as a grey band rather than as a shadow. assets/site.css solved
     this for the page's own card rows and the numbers come from there: a 24px
     rail either side and 64px of clearance below, with the extra handed back to
     the layout in negative margin so nothing under it moves. The side inset is
     margin here rather than bleed, so the cards land exactly where they landed
     without it and the track's own edges stop on the page's outer rail, which is
     where `.article-next` already stops. */
  .article-summary[data-m-swipe] {
    border: 0;
    background: none;
    box-shadow: none;
    padding: 0 24px 64px;
    margin: 32px -24px -40px;
    scroll-padding-left: 24px;
  }

  .article-summary[data-m-swipe] > div {
    border: 1px solid var(--art-hair);
    background: #fff;
    box-shadow:
      0 1px 2px rgba(10, 16, 60, .05),
      0 6px 14px rgba(10, 16, 60, .04),
      0 20px 46px rgba(10, 16, 60, .08);
  }

  .article-summary[data-m-swipe] > div::after {
    top: 16px;
    right: 16px;
  }
}

/* ==========================================================================
   PHONE: the article's white space, cut back.

   Reviewed as "on mobile, please save on white space, unnecessary space usage
   shall be cut", and again on the closing band as "above and below the section,
   remove the white space, it's too much".

   Measured on a 390px screen before this block, the article spent 894px of a
   9,261px document on vertical chrome alone: a 126/48 head, a 64/64 body, 54px
   above AND below each of the two figures, a 56/56 closing band, and a 44px
   margin plus 26-32px of padding on each of the three closing blocks. Every
   number below is that rhythm at roughly two thirds, which is the step the rest
   of the site already takes between desktop and phone. Nothing is removed, only
   tightened, and the desktop composition is untouched.
   ========================================================================== */
@media (max-width: 640px) {
  .article-head {
    padding-top: 104px;
    padding-bottom: 30px;
  }

  .article-body {
    padding-top: 40px;
    padding-bottom: 40px;
  }

  .article-body figure {
    margin-top: 32px;
    margin-bottom: 32px;
  }

  .article-sources {
    margin-top: 30px;
    padding-top: 20px;
  }

  /* `.article-body > .article-next` further up this sheet already sets 44px at
     child-combinator specificity, so the plain class cannot reach it */
  .article-body > .article-next,
  .article-next {
    margin-top: 30px;
    padding-top: 24px;
    padding-bottom: 24px;
  }

  .article-cta {
    margin-top: 24px;
    padding-top: 20px;
  }

  /* the closing band carries its own 56px on both sides on top of the section
     padding around it, which is the "too much" the review names */
  .cta-closing {
    padding-top: 34px;
    padding-bottom: 34px;
  }
}

/* ==========================================================================
   PHONE: the five-shift hero is one graphic, not a swipe.

   Reviewed as "please do not make this swipeable, make it as one graphic that
   works on mobile". It carried `data-m-swipe`, so the five columns became five
   full-width panels on a scroll-snap track and the hero showed one shift at a
   time behind a dot rail -- the argument of the figure is that the five are
   connected, and a carousel is the one form that cannot say that.

   Stacked, each shift is one compact row: the index and the title share a line,
   the supporting line sits under it, and a hairline divides them. Five rows at
   roughly 62px rather than five screens.
   ========================================================================== */
@media (max-width: 640px) {
  .five-shifts-graphic {
    display: block;
    width: calc(100% - 36px);
    padding: 6px 0;
  }

  .five-shifts-graphic article {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline;
    column-gap: 12px;
    padding: 12px 20px 13px;
    border: 0;
    border-top: 1px solid rgba(255, 255, 255, .2);
  }

  .five-shifts-graphic article:first-child {
    border-top: 0;
  }

  /* the rule between shifts is the row's own top border now, so the drawn
     hairline inside each card has nothing left to separate */
  .five-shifts-graphic i {
    display: none;
  }

  .five-shifts-graphic b {
    grid-column: 1;
    grid-row: 1;
    margin-bottom: 0;
  }

  .five-shifts-graphic h2 {
    display: block;
    grid-column: 2;
    grid-row: 1;
    font-size: 17px;
    line-height: 1.15;
  }

  .five-shifts-graphic p {
    grid-column: 2;
    grid-row: 2;
    margin-top: 3px;
    font-size: 11.5px;
    line-height: 1.4;
  }
}

/* ==========================================================================
   PHONE: the orchestration figure is one graphic, not a swipe.

   Reviewed as "do not make this swappable, make it as one graphic that fits in
   here". It carried `data-m-swipe`, so the five parts of one diagram -- the
   signal list, the wire in, the engine, the wire out, the outputs -- became
   five separate screens. The figure's whole claim is that signals go in, a
   decision comes out; a carousel shows the parts and hides the sentence.

   Stacked, it reads top to bottom in the same order it reads left to right on a
   desktop. The two wire assemblies are dropped rather than rotated: each one is
   a horizontal fan of five (or three) hairlines converging on a trunk, and that
   construction has no vertical equivalent. A single short connector above and
   below the engine says the same thing in the space a phone has.
   ========================================================================== */
@media (max-width: 640px) {
  .afig-orch {
    grid-template-columns: minmax(0, 1fr);
    justify-content: stretch;
    align-items: stretch;
  }

  .afig-orch .orch-wire {
    display: none;
  }

  .afig-orch .orch-signals {
    grid-template-rows: auto;
  }

  .afig-orch .orch-signals li {
    min-height: 0;
    padding: 9px 0;
    gap: 10px;
    font-size: 12.5px;
  }

  .afig-orch .orch-signals svg {
    width: 16px;
    height: 16px;
  }

  /* the connector, drawn on the engine so it cannot drift from it */
  .afig-orch .orch-engine {
    min-height: 0;
    margin: 22px auto;
    padding: 14px 16px;
    gap: 9px;
  }

  .afig-orch .orch-engine::before,
  .afig-orch .orch-engine::after {
    content: "";
    position: absolute;
    left: 50%;
    width: 1px;
    height: 14px;
    background: var(--fig-line);
  }

  .afig-orch .orch-engine::before { top: -18px; }
  .afig-orch .orch-engine::after { bottom: -18px; }

  .afig-orch .orch-engine svg { width: 22px; height: 22px; }
  .afig-orch .orch-engine span { font-size: 12.5px; }

  .afig-orch .orch-out {
    grid-template-rows: auto;
  }

  .afig-orch .orch-out li {
    padding: 9px 0 9px 0;
  }
}

/* ==========================================================================
   PHONE: the adoption ladder reads the phase you are on.

   Three reviews on one figure.

   "Give this round corners at the bottom so it aligns with the frame": the
   wedge is an <svg> with square corners sitting on the floor of a 16px-radius
   card, so its two bottom corners overhung the frame. The scale clips to the
   card's own radius.

   "The dots are too close to the separation line. Please remove the separation
   line. Have the dots exactly in the middle, between the text and the graphic":
   the rule goes and the dot rail takes equal air above and below.

   "Please have this like a battery that when swiped to augmentation, it shows
   that manual effort goes down and trust goes up ... find a way to showcase
   this dynamically based on what is selected": the wedge measures all four
   phases at once, which is the right drawing when all four are on screen and
   the wrong one when a phone shows a single phase. It is replaced here by two
   meters that read the selected phase, and the two chevrons either side of the
   dots say the track moves -- drawn with the two-border construction the
   home-page drop-downs use, so it is the same mark.
   ========================================================================== */
@media (max-width: 640px) {
  .phase-scale {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
    border-radius: 0 0 12px 12px;
    overflow: hidden;
  }

  /* the wedge is the all-four drawing; the meters are the one-phase drawing */
  .phase-scale > svg,
  .phase-scale .ps-a,
  .phase-scale .ps-b {
    display: none;
  }

  .ps-meters {
    display: grid;
    gap: 10px;
    padding: 14px 14px 15px;
    border-radius: 12px;
    background: var(--fig-tile-2);
  }

  .ps-meter {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 8px 10px;
  }

  .ps-meter-lab {
    grid-column: 1;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: -.005em;
    color: var(--fig-ink);
  }

  .ps-meter-bar {
    grid-column: 1 / -1;
    display: block;
    height: 8px;
    border-radius: 999px;
    background: rgba(47, 44, 43, .12);
    overflow: hidden;
  }

  .ps-meter-bar > i {
    display: block;
    height: 100%;
    border-radius: inherit;
    /* the width IS the reading; the transition is what makes it a battery
       rather than four separate pictures */
    transition: width .42s var(--ease-out-quint, cubic-bezier(.22, 1, .36, 1));
  }

  .ps-meter--manual .ps-meter-bar > i {
    width: var(--ps-manual, 90%);
    background: rgba(47, 44, 43, .45);
  }

  .ps-meter--trust .ps-meter-bar > i {
    width: var(--ps-trust, 20%);
    background: var(--blue);
  }

  @media (prefers-reduced-motion: reduce) {
    .ps-meter-bar > i { transition: none; }
  }

  /* ---- the dot rail, centred in its own gap, with the swipe chevrons ---- */
  .afig-phases + .m-dots,
  .afig-card .afig-phases ~ .m-dots {
    position: relative;
    margin-top: 16px;
    margin-bottom: 16px;
  }

  /* `background: none` and `border-radius: 0` are not defensive noise: a
     `.m-dots::after` rule further up this sheet paints a filled 999px dot, and
     without resetting it the right-hand chevron rendered as a fifth, darker
     dot on a four-dot rail. */
  .afig-card .afig-phases ~ .m-dots::before,
  .afig-card .afig-phases ~ .m-dots::after {
    content: "";
    position: absolute;
    top: 50%;
    width: 7px;
    height: 7px;
    margin-top: -4px;
    background: none;
    border: 0;
    border-radius: 0;
    border-right: 1.6px solid rgba(47, 44, 43, .38);
    border-bottom: 1.6px solid rgba(47, 44, 43, .38);
    pointer-events: none;
  }

  /* The site's chevron is a border-right + border-bottom corner, whose point
     sits down-right unrotated: 45deg is the closed drop-down (down), 225deg the
     open one (up). Turned a quarter either way it becomes the pair here --
     135deg points left, -45deg points right. */
  .afig-card .afig-phases ~ .m-dots::before {
    left: 50%;
    margin-left: -46px;
    transform: rotate(135deg);
  }

  .afig-card .afig-phases ~ .m-dots::after {
    left: 50%;
    margin-left: 40px;
    transform: rotate(-45deg);
  }
}

/* PHONE: the two mesh drawings come down a step.
   Reviewed as "please make a little smaller" on both, and "check that the arrow
   is not going into the next box" on the chained one -- that second point is
   fixed in the markup, where the two arrowheads were drawn 4 units past the
   edge of the box they point at and sat inside it. */
@media (max-width: 640px) {
  .mesh-svg {
    width: 84%;
    max-width: 250px;
    margin-inline: auto;
  }
}

/* PHONE: the gate figure's three notes.
   Reviewed as "this is no lower border ... fix" on the third row. The base
   sheet drops the bottom border on `:last-child`, which is right for a stacked
   list -- the container's own edge closes it -- and wrong here, where the track
   shows ONE row at a time: rows one and two were underlined and row three was
   not, so the rule appeared and disappeared as you swiped. Every row carries it
   in the track, so all three read identically. */
@media (max-width: 640px) {
  .fnotes[data-m-swipe] li:last-child {
    border-bottom: 1px solid var(--fig-line);
  }
}

/* PHONE: the atomic-content drawing comes down a step.
   Reviewed as "feel free to make this a little smaller on mobile, but it looks
   great already" -- so the composition is untouched and only its scale moves,
   the same 84% the two mesh drawings take. */
@media (max-width: 640px) {
  .atomic-svg {
    width: 84%;
    max-width: 260px;
    margin-inline: auto;
  }
}

/* The adoption meters are the phone drawing of the ladder; above 640px the
   wedge is the drawing and these are not part of it. Declared outside the phone
   block, like `.lead--phone` in assets/subpage.css, so the default is hidden
   and the phone cut turns it on -- otherwise the markup renders as three
   unstyled rows under the wedge at every desktop width. */
.ps-meters { display: none; }

/* ...and turned on again here, after that default, at one class more so the
   order of these two blocks cannot decide it. */
@media (max-width: 640px) {
  .phase-scale .ps-meters { display: grid; }
}

/* ==========================================================================
   PHONE: the next-article card, aligned.

   Reviewed as "this card looks very unaligned, please fix it" and "the white
   space in between here looks a little weird and off".

   One root cause for both. The block is built as a single row -- thumbnail,
   copy, arrow -- with the arrow parked at `grid-area: 1 / 2; justify-self: end`
   and `> div` reserving 60-68px of padding for it. The 620px cut then turns the
   inner row into a column but leaves those two facts alone, so on a phone:

     thumbnail   24..214   top 335   (a fixed 190px in a 282px column)
     copy        24..306   top 456
     arrow      322..366   top 428   (centred on the whole 280px block)

   Three elements at three different alignments, none sharing an edge with
   another, and 60px of padding held for an arrow that is no longer in that row.

   Lifted into one grid instead: the thumbnail spans the content column, and the
   copy and the arrow share the second row, centred against each other. Every
   part now starts on the 24px rail and the arrow closes on the right one.
   `display: contents` dissolves the two wrappers so all three become items of
   the same grid -- which also retires the padding reserve, since a contents box
   generates no box to pad.
   ========================================================================== */
@media (max-width: 640px) {
  .article-body > .article-next {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 16px;
    row-gap: 14px;
  }

  .article-next > div,
  .article-next .an-row {
    display: contents;
  }

  .article-next .an-thumb {
    grid-area: 1 / 1 / 2 / -1;
    width: 100%;
    /* 2.4:1 rather than the row's 16:9: full width at 16:9 is 192px tall
       against the 107px the 190px thumb used, and this pass is meant to be
       cutting height, not adding it. 2.4 lands at ~142px. */
    aspect-ratio: 2.4 / 1;
  }

  .article-next .an-copy {
    grid-area: 2 / 1 / 3 / 2;
  }

  .article-next > a {
    grid-area: 2 / 2 / 3 / 3;
    justify-self: end;
    align-self: center;
  }
}
