/*
Block 07 "Ready to begin your transformation?". Spec: docs/blocks/07-book.md.
Every value comes from tokens.css.

As everywhere on this site, what is written here is only where things sit and how they get
there. The heading is the shared two-tone component, the button is the shared button, the
entrance is the shared Reveal, and the blur the pictures come out of is the Photo reveal's
own — see the note under "Coming out of the middle".

--- The band is the section's own, not the page's --------------------------------------

This is the one block that paints its own background. The design gives it a radial band of
--surface-mist fading to white at the top and the bottom edges of the section, rather than
the page canvas with the aurora over it, so the section carries the gradient and the
gradient is measured against the section's own box.

That is why the room above the block is a margin and not the padding every other section
uses: padding would grow the box the gradient is measured in, move its centre and take the
whole portrait field with it.

It also leaves a seam. The gradient reaches white at the section's edges while the canvas
above and below it is --page-bg, some nine levels off white — the same page-level
discrepancy block 06 records, where the design's canvas is white and ours is not. It is a
decision about the page rather than about this block and is left alone here.

--- One cell, three layers ---------------------------------------------------------------

The section is a single grid cell holding the portrait field and the content one over the
other, the arrangement blocks 05 and 06 both use. The field comes first in the markup and
neither layer is positioned, so the content paints over it with no z-index to maintain.

The cell is stated as minmax(0, 1fr) rather than 1fr because the field is wider than the
viewport at anything under the design frame: a plain 1fr track takes its size from the
largest item in it, which would have made the row 1920 wide and pushed the page sideways.
Bounded at zero the track is the section's own width and the field overflows it, centred,
which is what `overflow-x: clip` is here to contain.
*/
.book {
  display: grid;
  margin-block: var(--book-top) var(--book-bottom);
  background: radial-gradient(
    var(--book-bg-rx) var(--book-bg-ry) at 50% 50%,
    var(--surface-mist) 0%,
    var(--surface-mist) var(--book-bg-stop),
    var(--surface) 100%
  );
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;

  /*
  Sideways only, so that the shadow under the lowest tiles — which sit on the very bottom
  edge of the frame, 1231.75 of 1232 — can still leave the section downwards. `clip` and not
  `hidden`: clip is the only value that can be paired with a visible other axis.
  */
  overflow-x: clip;
}

/*
The portrait field, at the design frame's own size and held there.

Fixed rather than fluid, and the reasoning is at --book-field-w in tokens.css: a field that
stretches with the viewport also shrinks with it, and shrinking drives the tiles onto the
content from both sides at once. Held at its own size the composition degrades the way the
design's own mobile frame does — the side columns slide off the edges and the bands across
the top and bottom stay where they are.

It is in flow, so the section is at least as tall as the field is, and the content beside it
can make the section taller without either of them being told how tall the other is.
*/
.book__field {
  position: relative;
  width: var(--book-field-w);
  height: var(--book-field-h);

  /*
  The field's own clock, set on the instance the way the Reveal component intends. A tile
  travels up to a thousand px where the component's default travel is 24, and the site's
  700ms over that distance is a throw rather than an arrival.
  */
  --reveal-dur: var(--book-tile-dur);
  --reveal-step: var(--book-tile-step);
}

.book__field--wide,
.book__content {
  grid-area: 1 / 1;
}

/* The two mobile bands do not exist above --bp-sm, and their pictures are never fetched. */
.book__field--top,
.book__field--bottom {
  display: none;
}

/*
A tile. Three numbers arrive on it from book.php — its position and its side, each as a
fraction of the field — and everything else is derived from those.

The backing colour is not decoration: it is what the shadow is cast by and what would show
if a picture failed to load, and the design gives it as the same --surface-mist the band
itself is made of.

The radius is a percentage, which on a square box is 18% of its own side — the one number
every row of the spec's radius column works out to. Stated once rather than twenty-four
times, and it survives the tiles being resized.
*/
.book__tile {
  position: absolute;
  top: calc(var(--ty) * 100%);
  left: calc(var(--tx) * 100%);
  width: calc(var(--tf) * 100%);
  border-radius: var(--book-tile-radius);
  aspect-ratio: 1;
  background: var(--surface-mist);
  box-shadow: var(--shadow-card);
  overflow: hidden;

  /*
  Where this tile comes from, and it is derived rather than declared: there is no list of
  twenty-four offsets anywhere, only this pair of expressions read against the tile's own
  --tx, --ty and --tf. Move a tile in the table and its flight moves with it.

  Both are percentages of the tile itself, because that is what a percentage in `translate`
  means, and the arithmetic is written to end up in those units:

      the gathering point is at   (gx, gy)          as a fraction of the field
      this tile's centre is at    (tx + tf/2, ...)   likewise
      so the distance is          (gx - tx - tf/2)   fractions of the field's width
      and the tile is             tf                 of the field's width
      hence                       (gx - tx - tf/2) / tf   of the tile's own width

  The vertical half has one more step in it. gy and ty are fractions of the field's height,
  not its width, so the difference is multiplied by the field's aspect before being divided
  by tf — the tile is square, so its height is tf of the field's width just as its width is.
  That multiplication is the whole reason --book-field-ratio exists.

  --reveal-shift is normally a length and here it is a percentage. That is the component's
  own knob, used as documented; the keyframe interpolates either.
  */
  --reveal-shift-x: calc(
    (var(--book-gather-x) - var(--tx) - var(--tf) / 2) / var(--tf) * 100%
  );
  --reveal-shift: calc(
    (var(--book-field-ratio) * (var(--book-gather-y) - var(--ty)) - var(--tf) / 2) /
      var(--tf) * 100%
  );
}

.book__photo {
  display: block;
  width: 100%;
  height: 100%;

  /*
  The files are square and cut to the design's own sizes, so nothing is cropped here. It is
  stated because a client-supplied replacement would not be, and a squashed portrait is a
  worse failure than a cropped one.
  */
  object-fit: cover;
}

/*
--- Coming out of the middle --------------------------------------------------------------

The tiles start gathered on one point, transparent and out of focus, and open out to their
places as the section is reached. Three components do it between them and none of it is
written from scratch: the fade and the flight are the Reveal's keyframe, the order is the
ordinal Reveal already stamps on every item, and the blur is the Photo reveal's own
`photo-reveal-sharpen` on the Photo reveal's own duration and curve.

What is not the Photo reveal's is the trigger, and that is deliberate rather than a
shortcut. photo-reveal.js watches each instance separately and starts it when that instance
is 35% on screen. On a frame 1232 tall with tiles from y 3 to y 1232 that is not one
entrance: the top row would sharpen a full screen of scrolling before the bottom row, while
the flight underneath it had already played for all of them on the section's own trigger.
The pictures come out of focus on the group's trigger instead, so the swarm opens and
resolves as one movement.

The blur is on the picture and not on the tile. A filter puts the element it is on onto a
composited layer of its own, and the tile is the box that carries the shadow — filtering it
would have meant rasterising the shadow's whole spread twenty-four times over, where this
way it is the 91-143px picture inside a clipped box.
*/
.reveal-js .book__field:not(.reveal--in) .book__photo {
  filter: blur(var(--book-tile-blur));
}

.reveal-js .book__field.reveal--in .book__photo {
  animation: var(--photo-reveal-blur-name) var(--photo-reveal-blur-dur)
    var(--photo-reveal-blur-ease) backwards;

  /*
  In step with the tile's own flight: the ordinal is the one the Reveal stamped on the tile
  above, inherited down. `backwards` holds the blurred first frame through the delay and no
  forwards fill, so the filter — and the layer it forces — is gone the moment the entrance
  is over instead of being carried for the life of the page. That is the Photo reveal's own
  arrangement, for its own reasons; see the note in photo-reveal.css.
  */
  animation-delay: calc(var(--reveal-index, 0) * var(--reveal-step));
}

/*
The content column, laid over the field. Placed from the top rather than centred in the
cell: measured on the design render the cap of the "R" sits 284.5 below the top of the
section against a field 1232 tall, which is 39px above the middle of it, and centring would
have been a decision the design does not take.
*/
.book__content {
  align-self: start;
  width: min(var(--book-content-w), 100% - var(--page-inset) * 2);
  margin-top: var(--book-content-top);
  text-align: center;
}

/*
The shared two-tone heading at this section's size, at its two trackings and in this
section's own accent.

The accent is --teal and not the --accent every other heading on the page carries, which is
the design's decision and not a reading error: sampled off the render the light half of this
heading gives 32/126/132 where the JPEG undershoots #2a8894, against the #5899b0 of block
06's. The tone stays the component's closed list; what the instance sets is which colour the
accent tone is here, the same way it sets which size the heading is.
*/
.book__title {
  max-width: var(--book-title-w);
  margin-inline: auto;

  --heading-accent: var(--teal);
  --heading-fs: var(--fs-book-title);
  --heading-ls: var(--ls-book-title);
  --heading-ls-accent: var(--ls-book-title-accent);
  --heading-lh: var(--lh-book-title);
}

.book__lead {
  max-width: var(--book-lead-w);
  margin: var(--book-lead-gap) auto 0;
  color: var(--ink-muted);
  font-size: var(--fs-book-lead);
  font-weight: var(--fw-regular);
  line-height: var(--lh-book-lead);
}

.book__cta {
  margin-top: var(--book-cta-gap);

  --btn-w: var(--book-cta-w);
  --btn-h: var(--book-cta-h);
}

/*
The contact row, and the rule above it is the list's own top border rather than a line of
its own: there is nothing between the two in the design, and a border cannot come apart from
the thing it belongs to.

Centred with a gap rather than spread with space-between. The design's three items happen to
fill the rule from end to end, which reads as a row justified to its edges and is not one:
the client rewrites these words, and under space-between a shorter middle item would push
the outer two apart while the dividers drifted with them.
*/
.book__contacts {
  display: flex;
  width: var(--book-contacts-w);
  max-width: 100%;
  margin: var(--book-rule-gap) auto 0;
  padding: var(--book-contacts-gap) 0 0;
  border-top: var(--rule-w) solid var(--book-rule-color);
  gap: var(--book-contacts-item-gap);
  justify-content: center;
  list-style: none;
}

/*
`flex: 0 0 auto` with the text held on one line, and both are load-bearing rather than
belt-and-braces. The design's three items and their two gaps come to 829.0 inside a row of
829 — equal to a tenth of a pixel — and a flex item at its natural size is allowed to shrink
by the rounding of that comparison. It did: the middle item lost a fraction of a pixel, its
last word went to a second line, and the row came back 58.31 tall instead of 29.16. Nothing
in the numbers said why, because every item was still on the right x.
*/
.book__contact {
  flex: 0 0 auto;
  white-space: nowrap;
  color: var(--ink-muted);
  font-size: var(--fs-book-contact);
  font-weight: var(--fw-regular);
  letter-spacing: var(--ls-book-contact);
  line-height: var(--lh-book-contact);
  text-transform: uppercase;
}

/*
The divider, drawn on every item but the first. Its own margin repeats the row's gap, so the
bar sits midway between two items rather than against the second one.
*/
.book__contact + .book__contact::before {
  content: "";
  display: inline-block;
  width: var(--book-sep-thick);
  height: var(--book-sep-len);
  margin-inline-end: var(--book-contacts-item-gap);
  border-radius: var(--book-sep-radius);
  background: var(--book-sep-color);

  /* The bar is centred on the row's own line, not hung off the text's baseline. */
  vertical-align: middle;
}

/*
Mobile: the field becomes two bands with the content between them. --bp-sm

The mobile frame draws seven tiles above the content and seven below it and leaves 438px
between the two groups, which the content does not fit into — see the note at
--book-band-ar in tokens.css. So the two groups become two rows of the grid rather than
two parts of one box, and each of them keeps the frame's own coordinates inside itself.

The bands stretch with the screen where the desktop field is held at its own size. They can:
a band is only as tall as its own tiles, so widening it moves nothing towards the content —
it only opens the row out sideways, which is what the design's own overhanging tiles are
already doing.
*/
@media (max-width: 700px) {
  .book {
    grid-template-rows: auto auto auto;
  }

  .book__field--wide {
    display: none;
  }

  .book__field--top,
  .book__field--bottom {
    display: block;
    width: 100%;
    height: auto;
  }

  /*
  Each band gathers its tiles at the edge nearest the content — the lower edge for the band
  above, the upper edge for the band below — so the two still read as one swarm opening out
  of the middle of the block rather than as two clusters spreading from their own centres.
  */
  .book__field--top {
    grid-area: 1 / 1;
    aspect-ratio: var(--book-band-ar);

    --book-field-ratio: var(--book-band-ratio);
    --book-gather-y: 1;
  }

  .book__content {
    grid-area: 2 / 1;
    margin-bottom: var(--book-band-gap);
  }

  .book__field--bottom {
    grid-area: 3 / 1;
    aspect-ratio: var(--book-band-ar);

    --book-field-ratio: var(--book-band-ratio);
    --book-gather-y: 0;
  }

  /*
  The divider lies down between the stacked items: what was 3 wide and 18 tall is 18 wide and
  3 tall, which is the one swap the two tokens were named for. It stops being an inline mark
  beside the text and becomes a row of the column.
  */
  .book__contacts {
    flex-direction: column;
    align-items: center;
  }

  .book__contact + .book__contact::before {
    display: block;
    width: var(--book-sep-len);
    height: var(--book-sep-thick);
    margin-block-end: var(--book-contacts-item-gap);
    margin-inline: auto;
  }
}

/*
No motion means the field is simply in place — in place and in focus. reveal.js declines to
mark the document at all in this case, so the rules above are unreachable; this is stated
anyway because the preference can be changed after the page has loaded, and because a blur
resolving is motion to anyone who asked not to be shown any.
*/
@media (prefers-reduced-motion: reduce) {
  .book__photo,
  .reveal-js .book__field:not(.reveal--in) .book__photo,
  .reveal-js .book__field.reveal--in .book__photo {
    animation: none;
    filter: none;
  }
}
