/**
 * patternpaste/chapters: the resting state.
 *
 * This is the finished composition a visitor sees if view.js never loads: a
 * two-column spread, every chapter's picture beside that chapter's own words,
 * in order, nothing hidden and nothing waiting to be undone. No opacity: 0, no
 * transform to reverse, no start state. Motion is added on top by view.js and
 * removed again by the revert.
 *
 * THE BLOCK STATES NO COLOUR AND NO FONT. Every mark is `currentColor`, mixed
 * down with `color-mix` where a surface is wanted, and the frame's ground is
 * TRANSPARENT: the composite's band is the ground, so the pictures sit in the
 * buyer's colour rather than ours. Every face is the theme's.
 *
 * THE RULE THE COMPOSITION ACTUALLY DEPENDS ON is a pair: a chapter is at
 * least `--patternpaste-band` tall and a resting picture is at most
 * `--patternpaste-band` tall. With one of each per row, the picture can never
 * decide a row's height, so lifting every picture out of the grid and into the
 * sticky frame at init cannot change the section's height by a pixel. That is
 * the whole of this block's answer to layout shift, and it is construction
 * rather than arithmetic. BOTH HALVES LIVE INSIDE THE TWO-COLUMN QUERY, because
 * that is the only composition that has a frame to lift a picture into: at one
 * column nothing is ever lifted, and a floor there is 68vh of empty page under
 * every short chapter on a phone.
 *
 * TWO COLUMNS ARE A BREAKPOINT AND NOT AN ATTRIBUTE, the same decision the
 * wall makes about its touch angles. Below it the spread is one column and the
 * DOM order (picture, prose, picture, prose) IS the plain stack, which is also
 * exactly what a reader gets with the plugin absent. view.js reads
 * `--patternpaste-columns` back off the resolved styles and does not
 * initialise at one, so the breakpoint is written here once and nowhere else.
 */

.wp-block-patternpaste-chapters {
  /* THE FOUR SIZES, as fluid clamps. render.php writes an inline copy only
   * when the author moved the control off the number the clamp settles at
   * (512, 640, 40, 56), so an untouched section stays fluid and one a buyer
   * has sized is exactly the numbers they set. */
  --patternpaste-stage: clamp(18rem, 54vh, 32rem);
  --patternpaste-band: clamp(24rem, 68vh, 40rem);
  --patternpaste-stick: clamp(1rem, 5vh, 2.5rem);
  --patternpaste-gap: clamp(1.5rem, 4vw, 3.5rem);

  /* THE SHAPE OF A FRAME WITH NOTHING TO DERIVE FROM. Every picture's own
   * ratio is written on its figure by render.php, from the image block's crop
   * or the attachment's natural size; this is what an EMPTY image block in a
   * fixture or a freshly inserted chapter falls back to, so the frame is a
   * frame before anybody has chosen a photograph. */
  --patternpaste-ratio: 1.5;

  /* HOW FAR THE CHAPTERS NOT BEING READ FADE, in the live state only. None
   * by default; render.php writes an inline copy when the author asked for
   * some, and the storefront's Motion drawer rewrites it in the browser. */
  --patternpaste-dim: 0%;

  /* Written inline by render.php: how many chapters there are, which is how
   * many explicit rows the grid lays, and which row each cell sits on. The
   * values here are only what a section rendered by something that is not
   * render.php would fall back to. */
  --patternpaste-chapters: 1;
  --patternpaste-row: auto;

  /* ONE COLUMN UNTIL THE MEDIA QUERY SAYS OTHERWISE, and view.js reads this
   * rather than a duplicate of the query. */
  --patternpaste-columns: 1;

  /* THE GUTTER, and it is the catalogue's own. A full-bleed block owns the
   * inset from the page's edge, because the composite's group ships
   * `padding-inline: 0` so the block can run the full width; `iso-wall` calls
   * the same number `--pv-gutter` and a composite's hand-off row is inset by it
   * too, so a section's masthead, its chapters and its frame all line up on one
   * edge. Not an attribute, for the same reason the breakpoint is not one: it
   * is the catalogue's measure rather than this block's decision. */
  --patternpaste-gutter: max(
    clamp(1.25rem, 4vw, 4rem),
    calc((100% - var(--patternpaste-measure)) / 2)
  );

  /* HOW WIDE THE SPREAD ITSELF IS ALLOWED TO GET, inside a band that is still
   * edge to edge. A two-column spread is a pairing, and a pairing whose columns
   * keep growing stops being one: measured 2026-09-03 in a 1990px window, the
   * rail track was 730px holding a 520px portrait, so a picture and the name
   * beside it sat half a screen apart with nothing in between. The band, the
   * masthead's ground and every colour a composite states run the full width as
   * before; only the columns stop. Not an attribute, for the same reason the
   * breakpoint and the gutter are not: it is the catalogue's measure rather
   * than this block's decision, and a section that wants its columns further
   * apart has a gap control that says so.
   *
   * IT IS THE GUTTER THAT CAPS IT, and that is the whole trick: past the
   * measure the inset grows to `(100% - measure) / 2`, so the content is
   * CENTRED in the window at exactly the measure, and below it the clamp is
   * still deciding both edges and nothing about a narrow screen changes. One
   * expression does the inset and the cap, which is what lets a composite's
   * masthead and hand-off row, core blocks OUTSIDE this one, write the same
   * `max(clamp(1.25rem, 4vw, 4rem), calc((100% - 1600px) / 2))` in their own
   * padding and land on the same two edges by construction. In px and not rem
   * so that the number a composite writes is the number this resolves to. */
  --patternpaste-measure: 1600px;

  /* THE MOUNT: how much ground shows between the drawn frame and the picture.
   * One number, used as padding at rest and as the pictures' inset when they
   * are stacked in the frame, because an absolutely positioned child is laid
   * out against its container's PADDING BOX and would otherwise cover the very
   * padding that makes the plate read as a mount. Measured on the first motion
   * frames: the resting picture had a mount and the live one had a hairline
   * and nothing else. */
  --patternpaste-mount: clamp(0.4rem, 0.9vw, 0.75rem);

  /* THE FRAME'S HEIGHT, CAPPED AGAINST THE VIEWPORT. A sticky frame taller
   * than the screen has a bottom the reader never reaches, so the stage height
   * is a request and this is what is honoured. `dvh` because a phone's toolbar
   * changes the answer while the reader scrolls. */
  --patternpaste-frame-h: min(var(--patternpaste-stage), calc(100dvh - 2 * var(--patternpaste-stick)));

  /* HOW FAR THE SCENE IS VEILED OUTSIDE THE CAMERA'S TARGET, camera mode
   * only. None by default; render.php writes an inline copy when the author
   * asked for some. `--patternpaste-spot-on` is view.js's, 1 on a chapter
   * with a target and 0 on one resting on the whole scene, so the veil lifts
   * on an interlude rather than sitting on a picture nobody is pointing at. */
  --patternpaste-spotlight: 0%;

  display: block;
  position: relative;
  /* THE BACKDROP FRAME MEASURES ITSELF AGAINST THIS BOX. `100cqw` is the
   * block's own width with no scrollbar in it, which is what a frame that
   * has to run edge to edge behind a padded grid needs and `100vw` cannot
   * give (the scrollbar's width of horizontal overflow, measured 2026-09-03). */
  container-type: inline-size;
}

/* The introduction sits above the spread, outside anything that sticks, so its
 * links and buttons are ordinary clickable content. */
.patternpaste-chapters__intro {
  box-sizing: border-box;
  padding-inline: var(--patternpaste-gutter);
  margin-block-end: var(--patternpaste-gap);
}

.patternpaste-chapters__intro > :first-child {
  margin-block-start: 0;
}

.patternpaste-chapters__intro > :last-child {
  margin-block-end: 0;
}

/* THE SPREAD. One column at rest and on a narrow screen: picture, prose,
 * picture, prose, in the order the author wrote them, which is the same stack
 * the plugin's absence leaves behind. */
.patternpaste-chapters__spread {
  display: grid;
  box-sizing: border-box;
  /* THE SAME GUTTER AS THE INTRO, at every width, so the masthead, the chapter
   * cells and the frame share one left edge. Measured on the first motion
   * frames: the intro and the prose ran to x=0 against the viewport's own edge
   * while the frame sat 120px in, which reads as two sections rather than
   * one. */
  padding-inline: var(--patternpaste-gutter);
  grid-template-columns: minmax(0, 1fr);
  gap: var(--patternpaste-gap);
  /* NOTHING HERE MAY CLIP. `position: sticky` is cancelled outright by an
   * ancestor with a scroll container of its own, and the failure is silent:
   * the frame simply scrolls away. */
  overflow: visible;
}

/* THE FRAME IS EMPTY UNTIL THE SCRIPT RUNS, and takes no space at all until
 * then. It holds nothing an author typed, so this hides no content: it is the
 * marquee's separator span the other way up, rendered on every render so the
 * storefront's Motion drawer has something to switch, and shown only in the
 * live state so a script-free reader is not given an empty box. */
.patternpaste-chapters__frame {
  display: none;
}

/* A CHAPTER. The floor half of the no-shift pair is in the two-column query
 * below, because that is the only composition it is a pair with: at one column
 * the picture is still in the flow, nothing is ever lifted out of the grid, and
 * a floor there is 68vh of nothing under every short chapter on a phone.
 * Reported 2026-09-03 on the case-study composite. */
.patternpaste-chapters__chapter {
  min-width: 0;
}

.patternpaste-chapters__chapter > :first-child {
  margin-block-start: 0;
}

.patternpaste-chapters__chapter > :last-child {
  margin-block-end: 0;
}

/* A PICTURE AT REST. The cap half of the no-shift pair: never taller than one
 * band, and never taller than the frame it is about to become, so the moment
 * the script lifts it into the frame nothing about it visibly changes either.
 * `max-width` and not `max-height`, because a box with an aspect ratio and a
 * capped height keeps its width and loses its shape; capping the width is the
 * same cap arrived at without distorting the frame. */
.patternpaste-chapters__figure {
  /* ALIGNED TO THE OUTSIDE EDGE OF ITS OWN TRACK, which is where the live frame
   * puts the shape: a picture that rested somewhere else would slide sideways
   * the moment the script landed. The outside edge is the section's gutter, so
   * a capped picture shares its left edge with the masthead above it rather
   * than floating in the middle of a much wider column, which is what the first
   * contact sheet showed at 1920. */
  margin: 0 auto 0 0;
  width: 100%;
  aspect-ratio: var(--patternpaste-figure-ratio, var(--patternpaste-ratio));
  max-width: calc(
    min(var(--patternpaste-band), var(--patternpaste-frame-h)) *
      var(--patternpaste-figure-ratio, var(--patternpaste-ratio))
  );
  overflow: hidden;
  box-sizing: border-box;
}

.patternpaste-chapters__figure figure,
.patternpaste-chapters__figure .wp-block-image {
  margin: 0;
  height: 100%;
}

.patternpaste-chapters__figure img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
}

/* A COVER OR A FRAME GROUP IN THE FIGURE. A cover brings its own minimum
 * height and flex layout, which would decide the figure's height over the
 * crop; inside the figure it is exactly the figure, and its image, which
 * WordPress positions absolutely, keeps the focal point the author set. A
 * group named Frame is whatever the author put in it, given the whole box. */
.patternpaste-chapters__figure .wp-block-cover,
.patternpaste-chapters__figure > .wp-block-group {
  margin: 0;
  width: 100%;
  height: 100%;
  min-height: 0;
  box-sizing: border-box;
}

.patternpaste-chapters__figure .wp-block-cover {
  padding: 0;
}

.patternpaste-chapters__figure .wp-block-cover .wp-block-cover__image-background,
.patternpaste-chapters__figure .wp-block-cover .wp-block-cover__video-background {
  width: 100%;
  height: 100%;
}

/* THE PLATE: the frame drawn. A ground mixed down from `currentColor` and a
 * hairline of the same, so it reads on the theme's own ground whichever way
 * round that ground is, and states no colour of its own. The padding is what
 * makes it a mount rather than an outline: an absolutely positioned picture is
 * laid out against the padding box, so the same rule dresses the resting
 * figure and the live shape. */
.wp-block-patternpaste-chapters[data-patternpaste-plate='true'] .patternpaste-chapters__figure,
.wp-block-patternpaste-chapters[data-patternpaste-plate='true'] .patternpaste-chapters__shape {
  padding: var(--patternpaste-mount);
  background-color: color-mix(in srgb, currentColor 7%, transparent);
  border: 1px solid color-mix(in srgb, currentColor 16%, transparent);
}

/* Off, the picture meets the page with nothing around it. */
.wp-block-patternpaste-chapters[data-patternpaste-plate='false'] .patternpaste-chapters__figure,
.wp-block-patternpaste-chapters[data-patternpaste-plate='false'] .patternpaste-chapters__shape {
  padding: 0;
  background-color: transparent;
  border: 0;
}

/* TWO COLUMNS, AND THE ONE PLACE THE BREAKPOINT IS WRITTEN. 55rem is where a
 * picture and a column of prose can both be read side by side; below it either
 * one is a sliver. */
@media (min-width: 55rem) {

  .wp-block-patternpaste-chapters {
    --patternpaste-columns: 2;
  }

  .patternpaste-chapters__spread {
    /* The prose column is the wider of the two: it is the half being read. */
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
    /* EXPLICIT ROWS, one per chapter, because `grid-row: 1 / -1` on the frame
     * reaches the end of the EXPLICIT grid only: with the rows left implicit
     * the frame spans one row and has nothing to stick down. */
    grid-template-rows: repeat(var(--patternpaste-chapters), auto);
    align-items: start;
  }

  /* The frame overlaps every figure cell in the rail column, which is legal
   * because both are definitely placed. It is empty until the script runs. */
  .patternpaste-chapters__frame {
    grid-column: 1;
    grid-row: 1 / -1;
  }

  .patternpaste-chapters__figure {
    grid-column: 1;
    grid-row: var(--patternpaste-row);
    align-self: start;
  }

  .patternpaste-chapters__chapter {
    grid-column: 2;
    grid-row: var(--patternpaste-row);
    /* THE FLOOR HALF OF THE NO-SHIFT PAIR, and it lives here because this is
     * the only composition it is a pair with. A chapter is at least one band
     * tall and a resting picture is at most one, so lifting every picture into
     * the frame cannot change a row's height. Band is the pacing knob as well
     * as the cap, so a chapter set taller than its picture is empty column ON
     * PURPOSE and the number to change is the composite's. */
    min-height: var(--patternpaste-band);
  }

  /* THE RAIL ON THE RIGHT. The whole of the flip: two columns swapped and the
   * cells sent the other way. Nothing about the DOM order moves, so the
   * reading order is picture-then-prose per chapter either way round. */
  .wp-block-patternpaste-chapters[data-patternpaste-flip='true'] .patternpaste-chapters__spread {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  }

  .wp-block-patternpaste-chapters[data-patternpaste-flip='true'] .patternpaste-chapters__frame,
  .wp-block-patternpaste-chapters[data-patternpaste-flip='true'] .patternpaste-chapters__figure {
    grid-column: 2;
  }

  .wp-block-patternpaste-chapters[data-patternpaste-flip='true'] .patternpaste-chapters__chapter {
    grid-column: 1;
  }

  /* AND THE OUTSIDE EDGE IS THE OTHER ONE when the rail is on the right, so the
   * picture still meets the section's own gutter rather than the gap between
   * the columns. */
  .wp-block-patternpaste-chapters[data-patternpaste-flip='true'] .patternpaste-chapters__figure {
    margin-inline: auto 0;
  }

  .wp-block-patternpaste-chapters[data-patternpaste-flip='true'].patternpaste-chapters--live .patternpaste-chapters__frame {
    justify-content: flex-end;
  }

  /* THE BACKDROP, and it exists only while the script is live: at rest and
   * on a phone it is the spread above. The frame spans both columns, runs the
   * block's full width by measuring itself against the block (`cqw`), and is
   * the height of the screen under its stick offset; the shape is that whole
   * box and every picture is cropped to fill it, as with the morph off. The
   * chapters keep their column and sit ABOVE it; what they sit on is the
   * composite's plate, because type never sits on a photograph. */
  .wp-block-patternpaste-chapters[data-patternpaste-backdrop='true'].patternpaste-chapters--live .patternpaste-chapters__frame {
    grid-column: 1 / -1;
    z-index: 0;
    width: 100cqw;
    margin-inline-start: calc(50% - 50cqw);
    height: calc(100dvh - 2 * var(--patternpaste-stick));
    justify-content: stretch;
    align-items: stretch;
  }

  .wp-block-patternpaste-chapters[data-patternpaste-backdrop='true'].patternpaste-chapters--live .patternpaste-chapters__shape {
    aspect-ratio: auto;
    width: 100%;
    height: 100%;
    max-height: none;
  }

  .wp-block-patternpaste-chapters[data-patternpaste-backdrop='true'].patternpaste-chapters--live .patternpaste-chapters__chapter {
    position: relative;
    z-index: 1;
  }
}

/* THE LIVE STATE. Everything below here is added by view.js and taken back off
 * by its teardown, and none of it is reachable without a script. */
.patternpaste-chapters--live .patternpaste-chapters__frame {
  display: flex;
  /* THE SHAPE HANGS FROM THE TOP OF THE FRAME, and that corner never moves.
   * Centred, every change of ratio moved the top edge as well as the bottom
   * (measured 2026-09-03 on the walkthrough: 39, 85 then 147px down the
   * viewport across three chapters), so a morph read as the whole picture
   * jumping about the rail rather than one edge travelling. Anchored, the
   * top-left corner is where the resting picture's was and only the far edges
   * move. */
  align-items: flex-start;
  /* THE SAME EDGE THE RESTING PICTURE HAD. Centring the shape in a track wider
   * than the picture put the live frame somewhere the still render never was,
   * so the section had two left edges and the pictures moved at init. */
  justify-content: flex-start;
  position: sticky;
  top: var(--patternpaste-stick);
  height: var(--patternpaste-frame-h);
  /* The frame is the sticky box and the shape inside it is what morphs, so the
   * box the judge measures never changes while the picture in it does. */
  box-sizing: border-box;
}

/* THE SHAPE, and the only thing in this block that changes size. Its aspect is
 * the current picture's, written by view.js as `--patternpaste-shape-ratio`;
 * the width is what carries the cap, for the same reason the resting figure's
 * does. */
.patternpaste-chapters--live .patternpaste-chapters__shape {
  position: relative;
  overflow: hidden;
  box-sizing: border-box;
  aspect-ratio: var(--patternpaste-shape-ratio, var(--patternpaste-ratio));
  width: min(
    100%,
    calc(var(--patternpaste-frame-h) * var(--patternpaste-shape-ratio, var(--patternpaste-ratio)))
  );
  max-height: 100%;
}

/* MORPH OFF: the frame is the stage box and every picture is cropped to fill
 * it. A composition for a set of pictures already in one crop, not the effect
 * switched off. */
.wp-block-patternpaste-chapters[data-patternpaste-morph='false'].patternpaste-chapters--live .patternpaste-chapters__shape {
  aspect-ratio: auto;
  width: 100%;
  height: 100%;
}

/* A PICTURE, ONCE THE SCRIPT HAS LIFTED IT INTO THE FRAME. Stacked one over
 * another so a swap can cross one into the next; which of them is painted is
 * view.js's, written as inline opacity and visibility and taken back off by
 * the revert. */
.patternpaste-chapters--live .patternpaste-chapters__shape .patternpaste-chapters__figure {
  position: absolute;
  inset: 0;
  margin: 0;
  /* THE PICTURE IS THE SHAPE'S BOX, stated as a height and not left to the
   * inset. The image block inside carries its own crop as an INLINE
   * `aspect-ratio`, which decides the picture's height from its width unless
   * every box above it has a definite height of its own: measured 2026-09-03
   * on the walkthrough, a 566x425 frame held pictures of 540x540, 540x405,
   * 540x720 and 540x304, so a portrait in a landscape frame was clipped to its
   * middle and a widescreen one left the plate bare beneath it, and a swap in
   * either direction showed the outgoing picture's travel as a strip of the
   * wrong photograph. Every box down to the img is the frame's now, and the
   * img's inline ratio is overruled below. */
  width: 100%;
  height: 100%;
  max-width: none;
  aspect-ratio: auto;
  padding: 0;
  border: 0;
  background: transparent;
  /* And it clips: the drift and the push are on the picture INSIDE this box,
   * so nothing a swap moves can paint over the mount or past the frame. */
  overflow: hidden;
}

.patternpaste-chapters--live .patternpaste-chapters__shape .patternpaste-chapters__figure figure,
.patternpaste-chapters--live .patternpaste-chapters__shape .patternpaste-chapters__figure .wp-block-image,
.patternpaste-chapters--live .patternpaste-chapters__shape .patternpaste-chapters__figure .wp-block-cover,
.patternpaste-chapters--live .patternpaste-chapters__shape .patternpaste-chapters__figure > .wp-block-group {
  width: 100%;
  height: 100%;
}

/* THE CAMERA'S PLANE. In camera mode one figure stays painted and view.js
 * moves the picture INSIDE it: a translate and a scale from the top-left
 * corner, so the point a chapter marks is carried to the middle of the frame
 * and the picture never shows an edge. The figure clips it. */
.wp-block-patternpaste-chapters[data-patternpaste-camera='true'].patternpaste-chapters--live
  .patternpaste-chapters__shape
  .patternpaste-chapters__figure
  img {
  will-change: transform;
  transform-origin: 0 0;
}

/* THE SPOTLIGHT: a veil in the page's own ink with a soft hole where the
 * camera is looking. Drawn by the shape's pseudo-element, so it is no node
 * of view.js's making; its centre is two custom properties view.js writes
 * during a swap and never between swaps. Above every picture, whatever
 * z-index a long run of swaps has handed them. */
.wp-block-patternpaste-chapters[data-patternpaste-camera='true'].patternpaste-chapters--live
  .patternpaste-chapters__shape::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 100000;
  pointer-events: none;
  background: radial-gradient(
    ellipse 26% 34% at var(--patternpaste-fx, 50%) var(--patternpaste-fy, 50%),
    transparent 38%,
    color-mix(in srgb, currentColor calc(var(--patternpaste-spotlight) * var(--patternpaste-spot-on, 1)), transparent) 100%
  );
}

.wp-block-patternpaste-chapters[data-patternpaste-camera='true'][data-patternpaste-plate='true'].patternpaste-chapters--live
  .patternpaste-chapters__shape::after {
  inset: var(--patternpaste-mount);
}

/* `!important` because the crop is an inline style on the img, written by the
 * image block itself, and inline beats every selector. Inside the frame the
 * crop is the SHAPE'S job: the img fills whatever shape the frame is at this
 * instant, mid-morph included, and `object-fit: cover` keeps the photograph
 * whole while it does. */
.patternpaste-chapters--live .patternpaste-chapters__shape .patternpaste-chapters__figure img {
  width: 100%;
  height: 100%;
  aspect-ratio: auto !important;
  object-fit: cover;
}

/* AND THE MOUNT SHOWS AROUND THEM. `inset: 0` is the shape's padding box, so
 * without this the picture covers the mount and the plate is a hairline with
 * nothing inside it. Insetting by the same number is what makes the live frame
 * the resting figure the reader was already looking at. */
.wp-block-patternpaste-chapters[data-patternpaste-plate='true'].patternpaste-chapters--live
  .patternpaste-chapters__shape
  .patternpaste-chapters__figure {
  inset: var(--patternpaste-mount);
  width: calc(100% - 2 * var(--patternpaste-mount));
  height: calc(100% - 2 * var(--patternpaste-mount));
}

/* THE CHAPTER BEING READ IS THE ONE THAT IS LIT. view.js marks the current
 * chapter cell in the live state and the teardown takes the mark back off; the
 * others fade by `--patternpaste-dim`, a percentage render.php writes only off
 * its default of none, so an untouched section dims nothing and a stylesheet
 * with no script has nothing to fade. The fade is a short transition that ends
 * inside one swap and never runs on its own: it starts when the reader crosses
 * a chapter boundary and at no other time, which is what keeps this block's
 * claim that nothing moves unattended true. Reduced motion never reaches the
 * live state at all. */
.patternpaste-chapters--live .patternpaste-chapters__chapter {
  transition: opacity 0.45s ease;
}

.patternpaste-chapters--live .patternpaste-chapters__chapter:not(.patternpaste-chapters__chapter--current) {
  opacity: calc(100% - var(--patternpaste-dim, 0%));
}

/* THE SCREEN: the one-column live state, for the camera and the backdrop
 * only. Below the breakpoint a spread is a plain stack and stays one, because
 * a frame beside a column needs a column beside it. A camera and a backdrop
 * need no column: the picture is the screen and the chapters pass over it on
 * their plates, which is the idiom every scroll piece uses on a phone, and it
 * is the same construction the backdrop uses at two columns. view.js adds the
 * class when it goes live at one column and takes it off again. The pictures
 * are lifted into the frame exactly as at two columns; what keeps the section
 * the same height is view.js writing each lifted picture's height (and the
 * gap it took with it) as an inline top margin on its chapter, restored by the
 * revert. Reported by Jake 2026-09-03: the camera composite on an iPad in
 * portrait showed its one photograph six times down the page, and the
 * backdrop composite was a spread it never is. */
.patternpaste-chapters--live.patternpaste-chapters--screen .patternpaste-chapters__spread {
  grid-template-rows: repeat(var(--patternpaste-chapters), auto);
}

.patternpaste-chapters--live.patternpaste-chapters--screen .patternpaste-chapters__frame {
  grid-column: 1;
  grid-row: 1 / -1;
  z-index: 0;
  width: 100cqw;
  margin-inline-start: calc(50% - 50cqw);
  height: calc(100dvh - 2 * var(--patternpaste-stick));
  justify-content: stretch;
  align-items: stretch;
}

.patternpaste-chapters--live.patternpaste-chapters--screen .patternpaste-chapters__shape {
  aspect-ratio: auto;
  width: 100%;
  height: 100%;
  max-height: none;
}

.patternpaste-chapters--live.patternpaste-chapters--screen .patternpaste-chapters__chapter {
  grid-column: 1;
  /* ON ITS OWN ROW, explicitly, as at two columns. The frame spans every
   * explicit row, and an auto-placed chapter would be put in the first free
   * cell AFTER them: measured 2026-09-03 at 820, the section grew by exactly
   * one screen and the plates started below the picture instead of over it. */
  grid-row: var(--patternpaste-row);
  position: relative;
  z-index: 1;
}

/* A PLATE OVER A PICTURE NEVER FADES. The fade dims the whole chapter cell,
 * plate included, and a plate at half strength over a photograph is
 * translucent type on a picture. On the screen every chapter is at full
 * strength whatever the fade says, and the same holds for the backdrop at
 * two columns for the same reason. */
.patternpaste-chapters--live.patternpaste-chapters--screen .patternpaste-chapters__chapter:not(.patternpaste-chapters__chapter--current),
.wp-block-patternpaste-chapters[data-patternpaste-backdrop='true'].patternpaste-chapters--live .patternpaste-chapters__chapter:not(.patternpaste-chapters__chapter--current) {
  opacity: 1;
}

/* THE EDITOR CANVAS. InnerBlocks owns the children, so the canvas cannot deal a
 * chapter's picture into a rail cell the way render.php does: what it shows is
 * the chapters themselves, in order, as the author wrote them. That is the
 * honest thing to show, because it is also the degraded stack and the phone
 * render. */
.patternpaste-chapters__canvas {
  display: flex;
  flex-direction: column;
  gap: var(--patternpaste-gap);
}

/* THE SLOT RULE, VISIBLE ON THE CANVAS. A top-level group is a chapter and
 * everything else is the introduction that renders ABOVE the spread, and an
 * author cannot act on that rule if both look the same. The editor writes the
 * block name onto every wrapper as `data-type`, which is the one place the
 * canvas can read the slot rule from. */
.patternpaste-chapters__canvas > .block-editor-block-list__block[data-type='core/group'] {
  padding-inline-start: clamp(0.75rem, 1.6vw, 1.5rem);
  border-inline-start: 3px solid color-mix(in srgb, currentColor 22%, transparent);
  margin: 0;
}

.patternpaste-chapters__canvas > .block-editor-block-list__block:not([data-type='core/group']) {
  margin: 0;
}

/* A PICTURE ON THE CANVAS IS CAPPED THE WAY THE PAGE CAPS IT. An empty
 * core/image is a placeholder that takes whatever width it is given, and the
 * first editor.png of this block was one grey box the height of the screen with
 * the chapters somewhere below it. The same cap the resting figure carries
 * makes the canvas read as the composition it is editing. */
.patternpaste-chapters__canvas > .block-editor-block-list__block[data-type='core/group'] .wp-block-image {
  max-width: calc(min(var(--patternpaste-band), var(--patternpaste-frame-h)) * var(--patternpaste-ratio));
  width: fit-content;
}

/* And the HEIGHT is the one that actually binds. An empty core/image with a
 * crop set is a placeholder carrying that crop as an `aspect-ratio`, so at 3:4
 * in a 645px column it is 860px tall and the first editor.png of this block was
 * one grey box with the chapters somewhere below it. Capping the height and
 * letting the width follow keeps the author's own crop while making the canvas
 * the size the page will be. */
.patternpaste-chapters__canvas > .block-editor-block-list__block[data-type='core/group'] .wp-block-cover {
  max-height: min(var(--patternpaste-band), var(--patternpaste-frame-h));
  min-height: 12rem;
}

.patternpaste-chapters__canvas > .block-editor-block-list__block[data-type='core/group'] .wp-block-image :is(img, .components-placeholder) {
  max-height: min(var(--patternpaste-band), var(--patternpaste-frame-h));
  width: auto;
  max-width: 100%;
}

/* The appender sits after the chapters rather than inside one of them. */
.patternpaste-chapters__add {
  display: flex;
  align-items: center;
}

/* An empty block still shows a column, so there is somewhere to drop the first
 * chapter into. */
.patternpaste-chapters.is-editing.is-empty .patternpaste-chapters__canvas {
  min-height: 8rem;
  border: 1px dashed color-mix(in srgb, currentColor 30%, transparent);
}

/* ON PAPER NOTHING STICKS. A script-free page prints as the spread it already
 * is; a live one has its pictures inside the frame, so they are put back into
 * the flow here and printed one under another rather than one on top of
 * another. */
@media print {

  .patternpaste-chapters--live .patternpaste-chapters__frame {
    position: static;
    display: block;
    height: auto;
  }

  .patternpaste-chapters--live .patternpaste-chapters__shape {
    aspect-ratio: auto;
    width: 100%;
    height: auto;
  }

  .patternpaste-chapters--live .patternpaste-chapters__shape .patternpaste-chapters__figure {
    position: static;
    width: 100%;
    opacity: 1;
    visibility: visible;
  }
}
