/* ============================================================
   FIGURES ON THIS PAGE

   One correction that only this case study needs. RAF has no placeholder left
   in it, so this does not belong in the shared sheet.

   NOT HERE, and worth recording: .cs-incol, the class that sets a tall figure
   INTO a single column, is gone from this page. Chromium and Safari balance
   the columns around it differently — Chromium put Fig. 01 at the head of
   column two, Safari at the foot of column one with column two left empty —
   and no amount of break-inside settles it. Every figure now spans the
   columns, which is what RAF does throughout and what both engines agree on.
   .cs-incol is used nowhere else on the site.
   ============================================================ */

/* A placeholder is scaffolding, not art.

   wide.css lifts --cs-bleed to 9999px so real figures can run to the viewport.
   An empty dashed box at that width reads as a fault rather than as a note
   about art that has not arrived — a metre and a half of nothing, with its
   caption stranded at the far left. A figure still holding a stand-in takes no
   bleed at all: it sits on the reading measure, the same width as the prose
   above it, which is unmistakably reserved space rather than a plate.

   Written against .cs-ph rather than a hand-added class so it lapses on its
   own: swap the placeholder for the real shot and the figure takes the full
   bleed with no second edit here. */
body[data-case-study] .cs-body .pv-fig:has(> .cs-ph) { --cs-bleed: 0px; }

/* A PORTRAIT placeholder gets narrowed as well as unbled. Fig. 01 stands in
   for a phone capture, and a 3:5 box at the full measure is over 1600px tall —
   a storey of empty page. Held to a believable phone width and centred, with
   its caption following it rather than running past it on both sides.

   .is-portrait in styles-sidebar.css is meant to do this and does not reach a
   spanning figure inside a chapter, so the width is stated here rather than
   changing a shared rule two other case studies depend on. */
body[data-case-study] .cs-body .pv-fig:has(> .cs-ph.is-phone) {
  width: min(100%, 360px);
  margin-inline: auto;
}

/* ============================================================
   THE FLOW, IN THREE SCREENS (ch3)

   Joining and leaving, each as three phone captures read left to right.
   Chapter three makes three claims (the hub, the offer in the cart, an exit
   that stays legible); the hub is Fig. 01 and these two carry the other
   two, so a reader who skips the paragraphs still sees them.

   The cards are RAF's .cs-trio, copied rather than linked. raf/before.css
   carries nine hundred lines this page does not use, and /raf is another
   page's folder; the same reasoning as wide.css. The textures are frames
   from the Factor film in the explainer above, not the RAF food shots, so
   the cards belong to this brand. The captures come from the local
   recording prototype (factor-plus/prototype, never deployed), exported
   by headless Chrome at 2x with the bezel, island and home bar removed.

   styles.css has an older generic .cs-trio grid; display: block here is
   what lets .cs-trio-row do the layout, as it does on RAF.
   ============================================================ */

.cs-trio { display: block; }

.cs-trio-row {
  display: grid;
  width: 100%;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 1.8vw, 24px);
  align-items: stretch;
}

.cs-trio-cell {
  --tex-top: rgba(14, 12, 10, 0.34);
  --tex-bottom: rgba(14, 12, 10, 0.82);
  position: relative;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 9% 9% 8%;
  border-radius: 18px;
  overflow: hidden;
  background: #171513;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 24px 50px -30px rgba(0, 0, 0, 0.5);
  isolation: isolate;
}

/* the texture, out of focus so it frames the capture instead of competing
   with it; negative inset because a blur samples past its own edge */
.cs-trio-cell::before {
  content: '';
  position: absolute;
  inset: -24px;
  background: var(--tex) center / cover no-repeat;
  filter: blur(7px);
  z-index: 0;
}

.cs-trio-cell::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, var(--tex-top), var(--tex-bottom));
  z-index: 0;
}

.cs-trio-shot {
  position: relative;
  z-index: 1;
}

.cs-trio-shot img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 14px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14), 0 18px 40px -18px rgba(0, 0, 0, 0.6);
}

.cs-trio-text {
  position: relative;
  z-index: 2;
  margin-top: 22px;
  color: #fff;
  font-family: var(--font-sans);
}

.cs-trio-label {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.3;
}

.cs-trio-line {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.9);
}

/* the arrival zoom is for photographs; it smears UI text while it lands */
.cs-fx .cs-body .pv-fig.cs-trio img { transform: none; transition: none; }

.cs-trio .pv-fig-cap {
  margin-inline: auto;
  text-align: center;
  max-width: 62ch;
}

@media (max-width: 760px) {
  .cs-trio-row { grid-template-columns: 1fr; gap: 14px; width: min(100%, 420px); margin-inline: auto; }
}

/* ============================================================
   WHERE IT STOPPED WORKING (ch5)

   The chapter's argument as one picture: value against control, one bar per
   loyalty level, crossing zero only at LL50+. The same head as RAF's chart
   (.cl-label / .cl-value / .cl-sub from styles-sidebar.css) so the two pages
   state a result in one voice. Bars rather than RAF's line because these are
   separate groups, not a series over time.

   Tonal, like RAF: the target cohort in --text-primary, the other bands in
   --text-muted, and the one negative bar in the page's single accent. The
   status column repeats the chapter's own verbs (expand, monitor, paused)
   so the recommendation reads without the paragraph.

   Geometry is set per row in the markup (--from, --w, as percentages of a
   -2 to +14 domain) rather than computed with abs(), which older Safari
   lacks. Zero sits at 12.5%. No motion: this page has none.

   Held to the reading measure: at full bleed the bars run a metre wide and
   the eye cannot compare their ends.
   ============================================================ */

body[data-case-study] .cs-body .pv-fig.cs-bands { --cs-bleed: 0px; }

.cs-bands .cl-panel {
  padding: clamp(20px, 3vw, 32px);
  border-radius: 18px;
  border: 1px solid var(--rule-soft);
  background: var(--surface);
}

.cs-bars {
  list-style: none;
  margin: clamp(16px, 2vw, 24px) 0 0;
  padding: 0;
  display: grid;
  gap: 10px;
  font-family: var(--font-sans);
  font-variant-numeric: tabular-nums;
}

.cs-bar {
  display: grid;
  grid-template-columns: 4.6em minmax(0, 1fr) 4.2em 7.5em;
  align-items: center;
  column-gap: 14px;
  font-size: 14px;
  line-height: 1.3;
  color: var(--text-muted);
}

.cs-bar-k { color: var(--text-primary); font-weight: 600; }

.cs-bar-track {
  position: relative;
  height: 22px;
}

/* the zero line */
.cs-bar-track::before {
  content: '';
  position: absolute;
  left: 12.5%;
  top: -5px;
  bottom: -5px;
  width: 1px;
  background: var(--rule);
}

.cs-bar-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--from);
  width: var(--w);
  border-radius: 4px;
  background: var(--text-muted);
  opacity: 0.55;
}

.cs-bar-v { text-align: right; color: var(--text-primary); font-weight: 600; }

.cs-bar.is-lead .cs-bar-fill { background: var(--text-primary); opacity: 1; }
.cs-bar.is-neg .cs-bar-fill { background: var(--accent); opacity: 1; }
.cs-bar.is-neg .cs-bar-v,
.cs-bar.is-neg .cs-bar-tag { color: var(--accent); }

@media (max-width: 620px) {
  /* the status drops under the band name so the bars keep their length */
  .cs-bar {
    /* wide enough for "Target cohort" on one line under the band name */
    grid-template-columns: 6.6em minmax(0, 1fr) 3.8em;
    grid-template-areas: "k track v" "tag track v";
    column-gap: 10px;
    font-size: 13px;
  }
  .cs-bar-k { grid-area: k; }
  .cs-bar-track { grid-area: track; }
  .cs-bar-v { grid-area: v; }
  .cs-bar-tag { grid-area: tag; font-size: 12px; }
}

/* ============================================================
   FIG. 02, THE JOIN CLIP

   Joining is the one moment on this page that needs motion: the total
   drops as the offer is taken, and stills cannot show that. So Fig. 02
   is a 9.6s loop recorded from the local prototype (real taps with a
   touch marker, 2x, see factor-plus/prototype/README.md), in the same
   blurred-film card as the trios, with the three beats beside it.
   demo.js lights the beat on screen while the clip plays; without it,
   or with the clip paused, all three stay at full strength.

   Held to the reading measure: one phone and a short list do not fill a
   full-bleed stage, and at that width the list would sit a long way from
   the thing it describes.
   ============================================================ */

body[data-case-study] .cs-body .pv-fig.cs-demo { --cs-bleed: 0px; }

.cs-demo-card {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 340px) minmax(0, 1fr);
  align-items: center;
  gap: clamp(28px, 5vw, 64px);
  padding: clamp(28px, 4.5vw, 56px);
  border-radius: 18px;
  overflow: hidden;
  background: #171513;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 24px 50px -30px rgba(0, 0, 0, 0.5);
  isolation: isolate;
}

.cs-demo-card::before {
  content: '';
  position: absolute;
  inset: -24px;
  background: var(--tex) center / cover no-repeat;
  filter: blur(7px);
  z-index: 0;
}

/* darker toward the list, which is where the white type sits */
.cs-demo-card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to right, rgba(14, 12, 10, 0.4), rgba(14, 12, 10, 0.86));
  z-index: 0;
}

.cs-demo-shot { position: relative; z-index: 1; }

.cs-demo-shot video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 784 / 1704;
  border-radius: 14px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14), 0 18px 40px -18px rgba(0, 0, 0, 0.6);
  cursor: pointer;
}
.cs-demo-shot video:focus-visible { outline: 2px solid #fff; outline-offset: 4px; }

.cs-demo-steps {
  position: relative;
  z-index: 2;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: clamp(20px, 3vw, 34px);
  color: #fff;
  font-family: var(--font-sans);
}

.cs-demo-steps li {
  display: grid;
  grid-template-columns: 2.4em minmax(0, 1fr);
  column-gap: 12px;
  transition: opacity 0.35s ease;
}

.cs-demo-n {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1.15;
  color: rgba(255, 255, 255, 0.6);
  font-variant-numeric: tabular-nums;
}

.cs-demo-steps .cs-trio-label { font-size: 17px; margin-bottom: 6px; }
.cs-demo-steps .cs-trio-line { font-size: 15px; line-height: 1.5; max-width: 34ch; }

.cs-demo.is-synced .cs-demo-steps li { opacity: 0.38; }
.cs-demo.is-synced .cs-demo-steps li.is-on { opacity: 1; }
.cs-demo.is-synced .cs-demo-steps li.is-on .cs-demo-n { color: #fff; }

/* the arrival zoom is for photographs */
.cs-fx .cs-body .pv-fig.cs-demo video { transform: none; transition: none; }

.cs-demo .pv-fig-cap {
  margin-inline: auto;
  text-align: center;
  max-width: 62ch;
}

@media (max-width: 760px) {
  .cs-demo-card { grid-template-columns: 1fr; justify-items: center; }
  .cs-demo-shot { width: min(300px, 84%); }
  .cs-demo-steps { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .cs-demo-steps li { transition: none; }
}
