/* The Lotus Den landing page. Extracted from lotus-den-poster.html —
   see lotus-den-handoff.md before changing the curve, the layer order,
   or any cqw value.

   This file WAS generated from that poster, but is now maintained here —
   the site has diverged from the print piece (real medallion artwork,
   smaller medallion type, no phone number). Do not regenerate over it. */
/* ============================================================
   THE LOTUS DEN — technique reference
   Pure CSS + SVG. No libraries, no raster assets.

   Four techniques carry the whole design:
     1. clip-path  ....... the sweeping curve that splits the page
     2. mask-image ....... soft / faded edges (badge, shadow, motifs)
     3. feTurbulence ..... paper grain + irregular torn edges
     4. mix-blend-mode ... making grain and shadow sit *in* the page

   Every length is written in cqw — 1cqw = 1% of the poster's own
   width — so the whole composition scales as one unit and there is
   not a single media query in the file.
   ============================================================ */

.stage {
  --linen: #e8d6c2;
  --linen-deep: #dcc7ae;
  --rust: #8d421d;
  --rust-deep: #7a3715;
  --ink-serif: #77391a;
  --ink-quote: #86401a;
  --ink-body: #3a2c22;
  --ink-olive: #3b3a24;
  --cream-text: #f0dcc2;
  --badge: #e7d2b4;
  --script: #3a3722;
  --phone: #7a3812;
  --gold: #c2a079;

  /* ---- DROP ANY PHOTOGRAPH IN HERE ----------------------------------
     Set --photo and the painted fallback scene disappears automatically.
     The photo needs no mask of its own: the cream and rust shapes paint
     on top of it, so the visible region is always the exact complement
     of the S curve. Change the curve and the photo window follows.

       --photo:      url("room.jpg");
       --photo-x:    56%;   horizontal framing (0% = left edge of photo)
       --photo-y:    50%;   vertical framing
       --photo-zoom: 1;     1 = fill the poster; >1 pushes in

     Zoom happens *about* the framing point, so pushing in keeps whatever
     you framed in place instead of drifting off.

     Framed for jennieMassageTestImage-leg-removed.png (896x1200): puts the
     wall mandala in the upper wedge and the head of the table below it.
     --photo-x is negative, which is legal and fine here: the photo stops
     short of the left edge, but the cream and rust shapes are complementary
     along the shared arc, so they always cover whatever the photo doesn't
     reach. Keep the image file next to this HTML.                       */
  /* The photograph is embedded as a data URI so this file is fully
     self-contained and works with no adjacent assets. To swap in a
     different image, replace the whole value with url("your.jpg"). */
  /* Framing only. The photograph itself is the <img class="photo-img">
     in the markup below — swap its src to change the image. */
  --photo-x: -27.2%;
  --photo-y: 26.9%;
  --photo-zoom: 1.3;
}

.stage * {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* body rule dropped — .stage handles placement; see build_poster.py */

.poster {
  /* Landing-page sizing: fit the viewport height, never overflow the width.
     1024/1536 is the poster aspect; --stage-reserve is room for the nav below. */
  width: min(100%, calc((100svh - var(--stage-reserve, 8rem)) * 1024 / 1536));
  margin-inline: auto;

  container-type: inline-size;
  position: relative;
  width: min(680px, 100%);
  aspect-ratio: 1024/1536;
  overflow: hidden;
  /* No drop shadow and no rounded corner. Both said "a printed sheet lying on a
     surface", which is what this stopped being: the page ground is now the same
     cream as the composition, so the cream simply continues past the edge and
     only the photograph and the rust read as having a boundary. */
  isolation: isolate; /* keeps blend modes contained */
  background: var(--linen);
}
.layer {
  position: absolute;
  inset: 0;
}

/* ---------- 1 · PHOTO (full bleed, underneath everything) ---------- */
.photo {
  overflow: hidden;
}
.photo-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover; /* same maths as background-size:cover */
  object-position: var(--photo-x) var(--photo-y);
  transform: scale(var(--photo-zoom));
  transform-origin: var(--photo-x) var(--photo-y); /* zoom about the framing point */
  display: block;
}
/* Painted stand-in. It sits BELOW .photo, so as soon as --photo is set
   the real image covers it. No JS, no toggle. */
.photo-scene {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Framing guide — shows where the photo will actually be visible.
   Delete this rule and the .guide element for production. */
.guide {
  z-index: 60;
  pointer-events: none;
  opacity: 0;
}
.poster.show-guide .guide {
  opacity: 1;
}
.guide path {
  fill: none;
  stroke: #22d3ee;
  stroke-width: 3;
  vector-effect: non-scaling-stroke;
}
.guide .halo {
  stroke: #0b1120;
  stroke-width: 7;
  opacity: 0.55;
}

/* ---------- 2 · CREAM SHAPE, cut by the curve ----------
   clipPathUnits="objectBoundingBox" writes the path in 0–1 space,
   so it stretches with the element and never needs recalculating. */
.cream {
  clip-path: url(#clipCream);
  /* The page paints this same gradient, in the poster's coordinates, so the two
     are one surface — see the body rule under "Landing-page chrome". Change the
     stops or the centre here and change them there. */
  background: radial-gradient(
    120% 90% at 14% 20%,
    #e3d3bc 0%,
    #dcc8b0 50%,
    #cbb699 100%
  );
}

/* ---------- 3 · RUST SHAPE ---------- */
.rust {
  clip-path: url(#clipRust);
  /* Soften only the RIGHT edge, where the band meets the photograph, so the
     room bleeds through instead of the block ending on a hard line.
     The angle (88deg) follows the edge, which leans right as it descends.
     The top edge stays crisp — that boundary is cut by the cream layer
     above, so the S curve keeps its definition. */
  -webkit-mask-image: linear-gradient(
    88deg,
    rgb(0, 0, 0) 0%,
    rgb(0, 0, 0) 42%,
    rgba(0, 0, 0, 0.92) 47%,
    rgba(0, 0, 0, 0.45) 52%,
    rgba(0, 0, 0, 0) 58%
  );
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
  mask-image: linear-gradient(
    88deg,
    rgb(0, 0, 0) 0%,
    rgb(0, 0, 0) 42%,
    rgba(0, 0, 0, 0.92) 47%,
    rgba(0, 0, 0, 0.45) 52%,
    rgba(0, 0, 0, 0) 58%
  );
  mask-repeat: no-repeat;
  mask-size: 100% 100%;
  background: radial-gradient(
    110% 80% at 24% 24%,
    #91441d 0%,
    var(--rust) 46%,
    #7a3715 100%
  );
}

/* ---------- 4 · PAPER GRAIN ----------
   feTurbulence generates the noise; overlay blend lets the colour
   underneath through, so it reads as fibre rather than a grey film. */
.grain {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 34cqw 34cqw;
  mix-blend-mode: overlay;
  opacity: 0.34;
  pointer-events: none;
}
/* second, directional pass gives the linen its slubby weave */
.weave {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='w'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.62' numOctaves='2'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23w)'/%3E%3C/svg%3E");
  background-size: 56cqw 56cqw;
  mix-blend-mode: soft-light;
  opacity: 0.55;
  pointer-events: none;
}

/* ---------- 5 · FOLIAGE SHADOW ----------
   Blurred blobs, multiplied onto the page, then faded out with a
   radial mask so the overlay itself has no visible edge.           */
.leafshadow {
  mix-blend-mode: multiply;
  opacity: 0.68;
  pointer-events: none;
  -webkit-mask-image: radial-gradient(
    76% 56% at 80% 4%,
    #000 40%,
    transparent 92%
  );
  mask-image: radial-gradient(76% 56% at 80% 4%, #000 40%, transparent 92%);
}

/* ---------- 6 · FLOWER OF LIFE ---------- */
.flower {
  position: absolute;
  left: 47.9cqw;
  top: 2.9cqw;
  width: 30.3cqw;
  height: 30.3cqw;
  opacity: 0.9;
  -webkit-mask-image: radial-gradient(
    circle at 52% 46%,
    #000 42%,
    transparent 82%
  );
  mask-image: radial-gradient(circle at 52% 46%, #000 42%, transparent 82%);
}
.flower circle {
  fill: none;
  stroke: var(--gold);
  stroke-width: 1.5;
}

/* ---------- 7 · MASTHEAD & BODY TYPE ---------- */
.lotus-top {
  position: absolute;
  left: 24.2cqw;
  top: 4.25cqw;
  width: 14.1cqw;
  color: var(--ink-quote);
}

.wordmark {
  position: absolute;
  left: 5.9cqw;
  width: 52cqw;
  text-align: center;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  color: var(--ink-serif);
  line-height: 1;
}
.the {
  top: 16.9cqw;
  font-size: 5.4cqw;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
}
.big {
  top: 21.1cqw;
  font-size: 13cqw;
  letter-spacing: 0.132em;
  text-indent: 0.132em;
}
.big.l2 {
  top: 31.9cqw;
}

.tagline {
  position: absolute;
  top: 52.26cqw;
  /* Shifted two characters left of where it started (5.5cqw). The line is
     centred inside this box, so moving the box moves the text; at font-size
     2.9cqw one Lato character is about 1.4cqw. The first move stopped the
     final "e" of "Massage" crossing onto the rust curve; the second is
     compositional. */
  left: 2.7cqw;
  width: 56cqw;
  text-align: center;
  font-size: 2.9cqw;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--ink-olive);
  white-space: nowrap;
}
.tagline b {
  font-weight: 400;
  color: var(--rust);
  padding: 0 0.7em;
}

.quote {
  position: absolute;
  top: 63.26cqw;
  left: 3cqw;
  width: 54.5cqw;
  text-align: center;
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 400;
  font-size: 4.3cqw;
  line-height: 4.6cqw;
  color: var(--ink-quote);
}

.body {
  position: absolute;
  left: 7.2cqw;
  width: 46cqw;
  white-space: nowrap;
  font-size: 2.62cqw;
  line-height: 3.35cqw;
  font-weight: 400;
  color: var(--ink-body);
}
/* Centred on the same axis as the rest of this column: the box spans
   7.2→53.2cqw, so its centre is 30.2 — the quote centres on 30.25, the tagline
   on 30.7. The block does not move; only the lines centre within it.

   p2 deliberately stays left-aligned. The cream is a wedge that closes to the
   right as it descends (its edge runs 53% of the poster width at 85cqw down to
   35% at 100cqw), and p2's lines get shorter as they go. Left-aligned, each
   short line ends further left and stays on the cream; centred, half the slack
   is pushed right and every line after the first lands on the photograph, dark
   ink on a dark chair. p1 sits higher where the wedge is still wide enough to
   centre in. */
.body.p1 {
  top: 80.46cqw;
  text-align: center;
  /* Centred in the cream rather than on the column axis the quote uses. The
     cream runs edge to edge on the left and is cut on the right by the arch, so
     its middle drifts as it descends — 27.21cqw at this paragraph's top row,
     25.88 at its middle, 24.26 at its foot, mean 25.78. The box is 46 wide, so
     it starts 23 left of that. Measure this against the rows the paragraph
     actually occupies: the same block sits 7cqw higher on a phone, where the
     wedge is wider and the answer is 26 (see the media query).

     Then right again, to sit half way between "centred" and the arc: from 3.11,
     where 1.54cqw of cream followed the final "e" of "presence.", to 3.88, which
     splits that gap and leaves 0.77. One Lato character here is 1.27cqw (line
     three sets 33 characters in 41.77cqw), so this is the finest of the moves —
     about six tenths of a character. Past roughly 4.6 the "e" touches the arc,
     which is the constraint this is balanced against. */
  left: 3.88cqw;
}

/* ---------- 8 · DIVIDER RULES ---------- */
.rule {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 1.3cqw;
}
.rule::before,
.rule::after {
  content: '';
  flex: 1;
  height: 1px;
  background: currentColor;
  opacity: 0.4;
}
.rule svg {
  width: 2.9cqw;
  height: 2.9cqw;
  flex: none;
}
.rule.r-head {
  top: 48.05cqw;
  left: 17.1cqw;
  width: 28.3cqw;
  color: var(--ink-quote);
}

.lotus-div {
  position: absolute;
  left: 27.2cqw;
  top: 73.16cqw;
  width: 8.1cqw;
  color: var(--ink-quote);
}

/* ---------- 9 · RUST-BAND CONTENT ---------- */
.band {
  position: absolute;
  /* Centred on the rust (which runs 0-52cqw at these rows, middle 26.5cqw),
     then moved 3cqw down and 3cqw right — a 45-degree shift of about 20px on
     each axis. Three quarters of an inch was asked for, which needs 51px of
     downward travel at 45 degrees; only 27px exists before the closing heart
     leaves the poster. This is the most that fits without retiming the spacing
     between the three blocks. */
  left: 4.25cqw;
  width: 44.5cqw;
  text-align: center;
  color: var(--cream-text);
}
.band .caps {
  text-transform: uppercase;
  font-size: 2.32cqw;
  letter-spacing: 0.19em;
  text-indent: 0.19em;
  line-height: 3.9cqw;
}
.band .ital {
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-size: 3.4cqw;
  line-height: 4.6cqw;
  /* Cormorant defaults to oldstyle figures, where the 9 descends and the 0 sits
     at x-height — "90" reads as "9o" at this size. The caps lines beside it use
     lining figures, so this also keeps the numerals consistent down the column. */
  font-variant-numeric: lining-nums;
}

.rule.r-b1 {
  top: 110.4cqw;
  left: 17cqw;
  width: 19cqw;
  color: var(--cream-text);
}
.band.b1 {
  top: 114.3cqw;
}
.rule.r-b2 {
  top: 122.8cqw;
  left: 17cqw;
  width: 19cqw;
  color: var(--cream-text);
}
.band.b2 {
  top: 124.7cqw;
}
.rule.r-b3 {
  top: 135.6cqw;
  left: 17cqw;
  width: 19cqw;
  color: var(--cream-text);
}
/* One line here now, where there were two, so this band and its rule move down
   together as a pair. Centring the line in the whole gap instead left the rule
   orphaned 3.35cqw above it — the other two sit 1.0cqw off their rules, and a
   divider that far from what it divides reads as belonging to nothing. The rule
   ends at 138.5, this clears it by that same 1.0, and the slack falls where it
   is wanted: between the closing line and the heart. */
.band.b3 {
  top: 139.5cqw;
}
/* The lotus that closed the cream column, repeated in cream on the rust.

   Sized to the heart it replaces rather than to the lotus above, which is much
   larger: matched on drawn height, since that is what sets the gap between the
   location line and the bottom of the poster. The heart drew 2.48cqw tall. This
   viewBox is 120x48 but the top petal reaches y=-5, outside it, so the drawn
   height is 49/48 of the box — 0.408 of the width once the 120:48 ratio is in.
   2.48 / 0.408 gives 6.08, rounded to 6. Being a wider mark than a heart, it
   comes out broader; that is the shape, not a size mismatch.

   left and top place the DRAWN mark, not the box. Centred on 26.5cqw, the axis
   the three rust lines centre on, and dropped so the ink sits where the heart's
   ink sat: the box starts 0.25cqw above its own drawing. */
.lotus-foot {
  position: absolute;
  top: 146.5cqw;
  left: 23.5cqw;
  width: 6cqw;
  color: var(--cream-text);
}

/* ---------- 10 · THE BADGE ----------
   The signature move. The mask composites a soft radial falloff with
   a turbulence-displaced edge, so the disc dissolves into the photo
   instead of stamping a hard circle on top of it.                   */
.badge {
  position: absolute;
  /* Sized and aligned to the lotus sunburst on the wall in the photograph, so
     the eye drops straight from the artwork to the medallion. Measured off the
     render: including its rays and sparkle points the sunburst spans
     70.6–95.8cqw horizontally and 38.9–65.7cqw vertically — a mean diameter of
     26cqw centred on 83.2cqw.

     Then nudged down a third of an inch by eye: 32px (CSS treats 1in as 96px)
     against the poster's 680px rendered width is 4.71cqw, so 116.77 became
     121.48. Expressed in cqw rather than px so the offset scales with the
     poster instead of drifting as the composition resizes. */
  left: 70.24cqw;
  top: 121.48cqw;
  width: 26cqw;
  height: 26cqw;
}
.badge-text {
  position: absolute;
  /* Exactly coincident with .badge, so its flex centring centres the contents
     in the disc rather than in a box offset from it. */
  left: 70.24cqw;
  top: 121.48cqw;
  width: 26cqw;
  height: 26cqw;
}
/* Real deckled-paper artwork, replacing the painted gradient disc. The file
   already carries its own ragged alpha edge, so the roughEdge turbulence
   filter and the border-radius are gone — either would fight it. Size is
   unchanged at 38.1cqw; only the surface differs.

   The mask feathers just the outermost few percent so the disc settles into
   the photograph instead of cutting against it. Every stop has exactly one
   position and uses rgba(0,0,0,0) rather than `transparent` — per the
   handoff, two-position stops and the `transparent` keyword make stricter
   parsers drop the whole declaration. */
.badge {
  background-image: url('/medallion.png');
  background-size: 100% 100%;
  background-repeat: no-repeat;
  -webkit-mask-image: radial-gradient(
    circle closest-side at 50% 50%,
    rgba(0, 0, 0, 1) 88%,
    rgba(0, 0, 0, 0.75) 95%,
    rgba(0, 0, 0, 0) 100%
  );
  mask-image: radial-gradient(
    circle closest-side at 50% 50%,
    rgba(0, 0, 0, 1) 88%,
    rgba(0, 0, 0, 0.75) 95%,
    rgba(0, 0, 0, 0) 100%
  );
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
.badge-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.badge-text .sun {
  width: 3.66cqw;
  color: var(--phone);
  opacity: 0.75;
}
/* Two lines instead of four leaves room, so the type is 1.55x what it was
   while the sun and the mark below grew only 1.25x — the ornaments stay subordinate to
   the words. Measured: the contents reach 77% of the disc radius, so the disc
   reads as full but not crammed. */
.badge-text .script {
  font-family: 'Caveat', cursive;
  font-weight: 600;
  font-size: 4.39cqw;
  line-height: 5.6cqw;
  color: var(--script);
  margin-top: 1.05cqw;
}
/* Matched to the heart it replaces on DRAWN height, not box width — the heart
   drew 2.047cqw tall inside a 2.73cqw box. This viewBox is 120x48 with the top
   petal at y=-5, outside it, so the drawing is 49/48 of the box and 0.408 of the
   width. 2.047 / 0.408 gives 5. The margin is larger than the heart's 2cqw
   because the heart's box padded its drawing from above while this one's
   overhangs it: 0.205cqw down there, 0.208cqw up here, so the margin absorbs
   both to leave the ink where it was. */
.badge-text .lotus {
  width: 5cqw;
  color: var(--phone);
  opacity: 0.85;
  margin-top: 2.41cqw;
}

/* ---------- 11 · UNIFYING PASSES ---------- */
.vignette {
  background: radial-gradient(
    120% 100% at 50% 45%,
    transparent 54%,
    rgba(70, 40, 18, 0.18) 100%
  );
  mix-blend-mode: multiply;
  pointer-events: none;
}

svg.linework {
  overflow: visible;
}
svg.linework path,
svg.linework circle,
svg.linework line {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}
@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
}

/* ── Landing-page chrome. Not part of the poster composition. ─────────── */

/* The page ground is the composition's own cream, so the poster stops reading
   as an object on a page and the page reads as the thing itself.

   It is the SAME radial gradient .cream paints, rewritten in page coordinates
   so it lands in the same place. A flat colour cannot do this: the cream's
   gradient changes with height, so any single colour matches at one row and
   drifts everywhere else — measured 9 levels out a fifth of the way down and 21
   by half, which is the seam getting stronger as it descends.

   The rewrite. The poster is min(680px, 100vw - 2rem) wide, 1.5 times that
   tall, centred by .stage, and 1rem from the top. Its gradient is
   "120% 90% at 14% 20%" of its own box, so against the page that is:

     radius x   120% of the poster's width          1.2  x its width
     radius y    90% of its height (1.5 x width)    1.35 x its width
     centre x   its left edge + 14% of its width    50% - 0.36 x its width
     centre y   its top edge + 20% of its height    1rem + 0.3  x its width

   Written as it is below for two reasons, both of which cost an afternoon.
   Chromium rejects multiplying a min() — calc(1.2 * min(680px, 100%)) is
   invalid and drops the whole declaration, leaving the page white — so each
   factor is distributed into its own min() instead. And percentages in a radial
   gradient resolve against the box's WIDTH for the x radius but its HEIGHT for
   the y radius, so a percentage cannot express "1.35 times the poster's width"
   vertically; vw is a length and has no such axis. Hence 120vw, not 120%.

   Because it is written against the poster's own width, it follows the poster
   at every size instead of being tuned for one.

   The stops are lifted from the ones .cream declares, because the grain
   (overlay, .34) and weave (soft-light, .55) sit over the poster and not over
   the page: the same declared colour renders four to ten levels lighter inside
   the poster than out. These are those values measured back. Change the grain or
   weave opacity and these need re-measuring — sample just inside the poster's
   edge and just outside, at the top of the cream and at the bottom of it.

   Set here rather than on the shared token because poster.css loads on the
   landing page alone — the About page keeps the site's own bone ground. Colour
   is a literal rather than var(--ink): that flips with prefers-color-scheme,
   and light text would be unreadable on a ground that is cream either way. */
body {
  /* The gradient is sized to the body's box, so all three of these matter. The
     box has to reach the bottom of the viewport or it ends where the content
     does and the canvas shows through; without no-repeat the gradient tiles
     past that box and the second tile's top edge draws a hard horizontal line
     across the page; and the colour catches anything either misses. */
  min-height: 100svh;
  background-color: #d2c0a7;
  background-repeat: no-repeat;
  background-image: radial-gradient(
    min(816px, calc(120vw - 2.4rem)) min(918px, calc(135vw - 2.7rem)) at
      calc(50% - min(244.8px, calc(36vw - 0.72rem)))
      calc(1rem + min(204px, calc(30vw - 0.6rem))),
    #e8dbc8 0%,
    #e1d1bd 50%,
    #d2c0a7 100%
  );
  color: #3a2c22;
}

.stage {
  --stage-reserve: 4rem;
  display: grid;
  place-items: center;
  padding: 1rem;
}

/* The wordmark is an h1 now; its three lines keep the poster's absolute
   placement, so the heading itself must not introduce a box of its own. */
.wordmark-group {
  margin: 0;
  font-weight: inherit;
}
.wordmark-group .wordmark {
  display: block;
}

/* The medallion is the page's only call to action. */
.badge-link {
  text-decoration: none;
  cursor: pointer;
  transition: transform 180ms ease-out;
}
.badge-link .script {
  /* Matches the wordmark rather than the olive --script, so the medallion reads
     as part of the same piece. Measured against the real paper pixels behind
     the text: worst case 3.92:1, 5th percentile 4.50:1. At the desktop size
     (28px) this is WCAG "large text", which needs 3.0 — comfortable. On a
     375px-wide poster the type falls to ~15.6px, where AA wants 4.5; #663015
     would clear that everywhere if it ever matters. */
  color: var(--ink-serif);
}
.badge-link:hover {
  transform: scale(1.02);
}
.badge-link:active {
  transform: scale(0.99);
}
.badge-link:focus-visible {
  outline: 0.5cqw solid var(--phone);
  outline-offset: 0.8cqw;
  border-radius: 50%;
}

@media (prefers-reduced-motion: reduce) {
  .badge-link {
    transition: none;
  }
}

/* ── The continuation band ───────────────────────────────────────────────
   A round photograph on the left with the second paragraph beside it.

   The motivation was a phone: a 2:3 poster on a tall screen leaves about a
   third of the view empty while the poster's own copy renders around 9px. This
   takes one paragraph out of the composition and sets it at a readable size in
   the space that was going to waste. It is not phone-only — the same move suits
   the desktop composition, so the band shows at every width.

   This was chosen over two alternatives (the poster carrying every paragraph,
   and the paragraph set over a full-width image) after comparing all three on a
   real device. The other two are gone; see git history if they are ever wanted.

   It lines up with the poster: .stage pads 1rem a side and the poster is
   min(680px, 100%) of what remains, so this repeats that sum rather than
   guessing a value that would drift if either changes. */
.continuation {
  width: min(680px, calc(100% - 2rem));
  margin: 0 auto;
  display: grid;
  grid-template-columns: 9rem 1fr;
  align-items: center;
  gap: 1.25rem;
  /* The indent moves the circle and the text right together. It is padding
     inside a box whose width still matches the poster's, so the right edge
     stays flush with the poster above and only the left edge comes in — the
     band keeps sitting squarely under the composition rather than drifting out
     from under it. */
  padding: 1.5rem 0 2rem 2rem;
}

.continuation__text {
  margin: 0;
  font-family: 'Lato', system-ui, sans-serif;
  line-height: 1.55;
}

.continuation .continuation__figure {
  margin: 0;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 50%;
}

.continuation .continuation__figure img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Framed by rendering the candidates rather than by eye. The image is 800x532
     and the frame is square, so 532px of its width shows. Both hands and the
     whole head span about 450px of that, so unlike the photograph this replaced
     there is slack: the choice is compositional rather than forced. 70% puts the
     hand resting on the head fully inside the circle, keeps the face complete,
     and leaves the lower hand its fingers — further left and those fingers meet
     the circle's edge, further right and the face slides off centre. There is no
     vertical overflow to position: cover scales this image by its height, so the
     second value does nothing here. */
  object-position: 70% 50%;
}

.continuation .continuation__text {
  font-size: 1rem;
  color: #3a2c22;
}

@media (max-width: 48rem) {
  /* The half-inch drop is a desktop move, and on a phone it has to come back.
     Enlarged (below), the first paragraph is a far taller block — four wrapped
     lines instead of three set ones — and at the dropped position its foot
     reaches 97cqw, where the cream wedge has closed to 42% of the poster width.
     Its last lines crossed onto the photograph. Putting the column back where
     it was drawn buys that height in the part of the wedge that is still wide. */
  .stage .tagline {
    top: 45.2cqw;
  }
  .stage .rule.r-head {
    top: 50.4cqw;
  }
  .stage .quote {
    top: 56.2cqw;
  }
  .stage .lotus-div {
    top: 66.1cqw;
  }

  /* Enlarged because 9px is too small to read, and shifted left because the
     wedge narrows as it descends. 46cqw from 3 centres the block on 26, which
     is where the cream's own mid-line falls across the rows it occupies (28.6
     at the top row, 24 at the bottom). Four lines, every one clear of the arc
     by at least 5.8cqw. Narrower boxes fit too but cost a fifth line and strand
     "presence." on its own. */
  .stage .body.p1 {
    white-space: normal;
    top: 73.4cqw;
    left: 3cqw;
    width: 46cqw;
    font-size: 3.4cqw;
    line-height: 4.2cqw;
  }

  /* Its <br>s were placed for the poster's own line lengths; once it wraps at a
     different width they fire as well as the wrapping and strand single words. */
  .stage .body.p1 br {
    display: none;
  }

  .continuation {
    grid-template-columns: 7.8rem 1fr;
    gap: 1rem;
    /* Same 20% growth and the same indent as the desktop rule, scaled down:
       a phone has far less width to give away, so the indent is half. */
    padding: 1.25rem 0 2rem 1rem;
  }

  .continuation .continuation__text {
    font-size: 0.95rem;
  }
}
