/* =========================================================================
   The plan

   The drawing is issued portrait — a tall, narrow bar — and at that
   proportion it towers over anything beside it. scripts/floorplan.py turns
   it on its side, and here it takes most, but deliberately not all, of the
   width: the figures that describe it sit in the column that leaves, on
   the right, rather than being stacked above empty space.

   The artwork is white linework on transparency, lifted out of the issued
   PDF by that same script. Nothing here recolours it; it is already the
   right colour for a black page.
   ========================================================================= */

.plan {
  /* The width of the right-hand column, shared by the model toggle and the
     figures below it so the two line up on their left edge by construction
     rather than by coincidence of how wide the button labels happen to be. */
  --plan-aside: clamp(14.5rem, 18vw, 16.5rem);
  --plan-gap: clamp(var(--space-6), 5vw, 4rem);

  padding: var(--section-pad) var(--gutter-safe);
  background: var(--c-black);
}

/* ---- The card ----
   The floor plan in a card of its own, as the exteriors and interiors
   are: inset from the page on its edges, the finish cards' surface behind
   it (finish-card.css) lit in smoke, and its contents inset from the
   card's edge as theirs are — a share of the card's width (cqw), so the
   insets are carried by the children (.plan.plan--card, to hold against
   the children's own margin shorthands): the card cannot size its own
   padding that way. The words before it sit in a lead of their own. */
.plan--card {
  --glow: var(--c-smoke);
  /* the finish cards' unit, and the band they keep over their surface for
     the tray (finish-card.css): the same here, so the three sit alike */
  --u: min(1cqw, 1.434vh);
  --card-top: calc(max(0.6875rem, calc(1.12 * var(--u))) + calc(1.66 * var(--u)));

  position: relative;
  isolation: isolate;
  display: flow-root;      /* the children's margins stay inside the card */
  container-type: inline-size;
  margin: var(--section-pad) var(--gutter-safe);
  padding: 0;
  background: none;
}

.plan.plan--card > * {
  margin-inline: 3.9cqw;
}

.plan.plan--card > :first-child {
  margin-top: calc(var(--card-top) + 4.09cqw);
}

.plan.plan--card > :last-child {
  margin-bottom: 2.4cqw;
}

@media (max-width: 47.99rem) {
  .plan.plan--card > * {
    margin-inline: 6.36cqw;
  }

  .plan--card {
    --card-top: 0px;
  }

  .plan.plan--card > :first-child {
    margin-top: 7.27cqw;
  }

  .plan.plan--card > :last-child {
    margin-bottom: 8cqw;
  }
}

.plan__lead {
  padding: var(--section-pad) var(--gutter-safe) 0;
  background: var(--c-black);
}

.plan__lead + .plan--card {
  margin-top: 0;
}

/* The label and the model toggle want clear air beneath them: the drawing
   starts at the top of its column, so without this the head sits almost on
   the roofline. */
.plan .section-head {
  margin-bottom: var(--head-gap);
}

@media (min-width: 48rem) {
  .plan .section-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--plan-aside);
    column-gap: var(--plan-gap);
    align-items: center;
  }

  /* The row keeps its right edge on the column that the figures below it
     use, and takes whatever width it needs to the left of that. It no
     longer fits the column: the two buttons used to divide it between
     them, but a prompt was added to the row later and there is not enough
     left for "SOLUM STUDIO" to set. Allowed to shrink it was cutting the
     word off by 21px. Growing leftward costs nothing — the only thing in
     that column is the heading, away at the far end. */
  .plan .swatches {
    width: max-content;
    justify-self: end;
  }

  .plan .swatch {
    flex: 0 0 auto;
  }
}

/* The sheet reference, sitting opposite the label like a drawing number */
.plan__sheet {
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: var(--fs-micro);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--c-ink-4);
}

/* ---- The drawing, and the figures beside it ---- */
.plan__body {
  display: grid;
  gap: var(--space-6);
}

/* =========================================================================
   The figures

   One list, two across, with a rule down the middle. It replaces the
   three-up lead that sat beside the drawing and the five-up strip that
   sat under it — between them they said the configuration twice and the
   internal area twice, which is what made the section feel padded.
   ========================================================================= */

.plan__facts {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--plan-gap);
  row-gap: var(--space-6);
  margin: 0;
  align-content: start;
}

/* The rule sits on the column gap rather than between two bordered
   cells, so it runs the height of the block whatever ends up in it. */
.plan__facts::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(50% - 0.5px);
  width: 1px;
  background: var(--c-hairline);
}

/* Two coats of noise, used by the slab and by the band below it.

   A long, even ramp has nowhere to put its rounding error, so it collects
   it into the steps you see as banding. Real noise laid over the top
   breaks the steps up — and at these frequencies it also does the work
   the name suggests: the coarse coat drifts across the box in clouds, the
   fine one is the grain in them. feTurbulence rather than another stack
   of gradients, because gradients repeat and smoke does not.

   Both are desaturated: turbulence comes out in colour otherwise. The
   site's CSP allows data: for images, which is what these are. */
.plan {
  --plan-smoke:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='280' preserveAspectRatio='none'%3E%3Cfilter id='s'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.014 0.035' numOctaves='4' seed='11'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='600' height='280' filter='url(%23s)' opacity='0.09'/%3E%3C/svg%3E");
  --plan-grain:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)' opacity='0.035'/%3E%3C/svg%3E");
}
/* ---- The drawing, and the sheet it sits on ----

   --plan-span holds the Studio a little smaller than the Standard, because
   it is a smaller building and the PDFs are drawn at unrelated scales so
   the artwork cannot say so by itself. It is not the true ratio — that is
   0.75, which leaves the Studio looking stranded. This is pulled back
   towards parity for the sake of continuity between the two. */
.plan__drawing {
  display: flex;
  align-items: center;
  /* the stage centred in its sheet, where a short screen holds it
     narrower than the sheet (--plan-stage-max, scene.js); inside it, both
     plans start at the same edge, so switching model reads as one building
     replacing another rather than as the whole drawing moving */
  justify-content: center;
  margin: 0;
  /* The proportions live on the stage inside, which is what actually
     holds the drawing; the figure is that plus its padding. */

  /* ---- The sheet the drawing sits on ----

     A drawing alone on black has nothing to sit against and reads as
     floating. This is the same glass the buttons are made of, at the size
     of a panel: a dark slab lit along its top edge, a hairline around it,
     and the corner taken off the bottom right as everywhere else.

     content-box, so the proportions above describe the drawing and not
     the slab. Measured on the border box instead, the padding would come
     out of the drawing, and since the slab is always full width while the
     drawing is not, the two models would stop matching in height and the
     figures below would shift on every toggle. */
  box-sizing: content-box;
  padding: clamp(1rem, 2.8vw, 2.25rem);
  /* deeper at the foot, to leave the band room for the pieces in it */
  --plan-foot: clamp(3.75rem, 7vw, 5.25rem);
  padding-bottom: var(--plan-foot);

  /* The room mix, and the bite in the band that holds it. The seam is the
     same 4-odd pixels the peek piece leaves on the pictures. */
  /* Opening a room is one move made of several parts. They share a
     duration so they cannot drift apart, and the supporting ones are
     delayed off it rather than given lengths of their own. */
  --plan-move: 0.9s;

  --chip: 2rem;
  --chip-gap: 6px;
  --plan-seam: 5px;
  --plan-bite-w: calc(3 * var(--chip) + 2 * var(--chip-gap) + var(--plan-seam));
  --plan-bite-h: calc(var(--chip) + var(--plan-seam));
  --plan-eye-w: calc(var(--chip) + var(--plan-seam));
  --plan-eye-h: calc(var(--chip) + var(--plan-seam));

  position: relative;
  /* No border and no edge light: the slab is held by the light in it,
     not by a line around it. */
  border-radius: var(--img-radius);
}

/* The light and the texture go on a layer of their own so they can be
   faded out before they reach the edges. Painted into the slab itself
   they hold their strength right up to its bounds, and even at a few per
   cent that draws a rectangle on the black — which is the one thing this
   is not supposed to be. Masking the slab instead would take the drawing
   with it. */
.plan__drawing::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background:
    var(--plan-grain) 0 0 / 140px 140px repeat,
    var(--plan-smoke) bottom / 100% calc(var(--plan-foot) * 2.4) no-repeat,
    /* Pools at sizes and offsets that share no common factor, so nothing
       lines up into a stripe. */
    radial-gradient(72% 96% at 16% 114%,
      rgb(255 255 255 / 0.24), transparent 64%)
      bottom / 100% calc(var(--plan-foot) * 1.5) no-repeat,
    radial-gradient(94% 84% at 74% 120%,
      rgb(255 255 255 / 0.2), transparent 62%)
      bottom / 100% calc(var(--plan-foot) * 1.5) no-repeat,
    /* Measured up from the foot rather than as a share of the box. The
       box grows when a room is opened, and a gradient pinned to
       percentages would climb with it and wash across the drawing. */
    linear-gradient(
      to top,
      rgb(255 255 255 / 0.4) 0,
      rgb(255 255 255 / 0.29) calc(var(--plan-foot) * 0.12),
      rgb(255 255 255 / 0.18) calc(var(--plan-foot) * 0.3),
      rgb(255 255 255 / 0.09) calc(var(--plan-foot) * 0.5),
      rgb(255 255 255 / 0.03) calc(var(--plan-foot) * 0.72),
      rgb(255 255 255 / 0) calc(var(--plan-foot) * 1));
  /* A bite out of the bottom-left corner for the room mix to sit in, the
     same move as the notch the peek piece sits in over on the pictures:
     the chips are the piece, the bite is a shade larger on its two inner
     sides, and what is left between them is an even seam. The reentrant
     corner is chamfered so the cut speaks the same angle as the buttons.

     The bite is measured off the chips themselves, so the seam holds
     whatever --chip and --chip-gap are set to. */
  /* Two bites out of the foot, one at each corner, for the pieces that
     sit in them: the room mix on the left and the eye on the right. Same
     move as the notch the peek piece sits in over on the pictures — each
     bite is a seam larger than the piece it holds.

     Every corner is rounded, which a polygon cannot do directly, so the
     arcs are sampled: four segments across each quarter turn, which at
     these radii is under two tenths of a pixel off true. The straight
     runs stay in calc so the bites keep measuring themselves off the
     pieces. Generated — if a radius or a chip size moves, regenerate it
     rather than nudging points by hand. */
  clip-path: polygon(
    0 0,
    100% 0,
    100% calc(100% - var(--plan-eye-h) - 6px),
    calc(100% - 0.46px) calc(100% - var(--plan-eye-h) - 3.7px),
    calc(100% - 1.76px) calc(100% - var(--plan-eye-h) - 1.76px),
    calc(100% - 3.7px) calc(100% - var(--plan-eye-h) - 0.46px),
    calc(100% - 6px) calc(100% - var(--plan-eye-h)),
    calc(100% - var(--plan-eye-w) + 10px) calc(100% - var(--plan-eye-h)),
    calc(100% - var(--plan-eye-w) + 6.17px) calc(100% - var(--plan-eye-h) + 0.76px),
    calc(100% - var(--plan-eye-w) + 2.93px) calc(100% - var(--plan-eye-h) + 2.93px),
    calc(100% - var(--plan-eye-w) + 0.76px) calc(100% - var(--plan-eye-h) + 6.17px),
    calc(100% - var(--plan-eye-w)) calc(100% - var(--plan-eye-h) + 10px),
    calc(100% - var(--plan-eye-w)) calc(100% - 6px),
    calc(100% - var(--plan-eye-w) - 0.46px) calc(100% - 3.7px),
    calc(100% - var(--plan-eye-w) - 1.76px) calc(100% - 1.76px),
    calc(100% - var(--plan-eye-w) - 3.7px) calc(100% - 0.46px),
    calc(100% - var(--plan-eye-w) - 6px) 100%,
    calc(var(--plan-bite-w) + 6px) 100%,
    calc(var(--plan-bite-w) + 3.7px) calc(100% - 0.46px),
    calc(var(--plan-bite-w) + 1.76px) calc(100% - 1.76px),
    calc(var(--plan-bite-w) + 0.46px) calc(100% - 3.7px),
    var(--plan-bite-w) calc(100% - 6px),
    var(--plan-bite-w) calc(100% - var(--plan-bite-h) + 10px),
    calc(var(--plan-bite-w) - 0.76px) calc(100% - var(--plan-bite-h) + 6.17px),
    calc(var(--plan-bite-w) - 2.93px) calc(100% - var(--plan-bite-h) + 2.93px),
    calc(var(--plan-bite-w) - 6.17px) calc(100% - var(--plan-bite-h) + 0.76px),
    calc(var(--plan-bite-w) - 10px) calc(100% - var(--plan-bite-h)),
    6px calc(100% - var(--plan-bite-h)),
    3.7px calc(100% - var(--plan-bite-h) - 0.46px),
    1.76px calc(100% - var(--plan-bite-h) - 1.76px),
    0.46px calc(100% - var(--plan-bite-h) - 3.7px),
    0 calc(100% - var(--plan-bite-h) - 6px));
  /* The texture would otherwise hold its strength to the top edge, and
     even at a few per cent that draws a rectangle on the black. */
  /* the texture fades in over the same distance, for the same reason */
  -webkit-mask-image: linear-gradient(to top,
    #000 calc(var(--plan-foot) * 0.55), transparent calc(var(--plan-foot) * 1.05));
  mask-image: linear-gradient(to top,
    #000 calc(var(--plan-foot) * 0.55), transparent calc(var(--plan-foot) * 1.05));
  pointer-events: none;
}

/* over the light */
.plan__drawing img {
  position: relative;
}

.plan__model[data-variant="studio"] {
  --plan-span: 0.88;           /* true ratio is 0.75 — see above */
}
/* =========================================================================
   The stage

   The drawing moves inside a window that never does. Going in close on a
   room is a transform on the plan, and the stage clips it, so the box the
   gradient is painted on keeps exactly the proportions it had — which is
   the whole trick: nothing below it moves when a room is picked.
   ========================================================================= */

.plan__stage {
  /* no wider than scene.js allows on a screen too short for the card,
     the drawing then a little smaller within its sheet (--plan-stage-max) */
  max-width: var(--plan-stage-max, none);
  position: relative;
  width: 100%;
  /* The drawing's own proportions at rest, and a shape a room can
     actually live in while one is open. The building is one room deep, so
     a room's depth is the building's depth: in the letterbox it can only
     be magnified by cutting its top and bottom off. Given room to grow,
     the same room goes to 2.25 and stays whole. */
  /* (the Plus's drawing; the Studio's, a shade squarer, sits within it,
     so the card keeps its size as the model changes) */
  aspect-ratio: 768 / 263;
  overflow: hidden;
  transition: aspect-ratio var(--plan-move) var(--ease-out);
}

/* Opening is announced by the class and the framing by the attribute,
   and they are set at different moments on purpose: the box starts
   growing the instant the chip is pressed, while the drawing does not
   change what it is showing until it has faded down far enough to hide
   the change. See ui/rooms.js. */
.plan__stage.is-open {
  aspect-ratio: 4 / 3;
}

/* The room, and only the room.

   A vignette in the page's own black, laid over the drawing while a room
   is open. It does two jobs at once, and they are the same job: the
   framing centres the room, so whatever sits at the edges of the frame is
   the rooms either side of it, and fading the edges both takes those down
   and turns the crop into something that stops rather than something that
   is cut.

   An overlay rather than a mask on the stage, because a mask would have
   to switch between two gradients and gradients do not interpolate — it
   would pop. Opacity does, so this fades in with the growth.

   The ellipse is wider than it is tall relative to the frame, so the
   sides go first, the top and bottom stay mostly readable, and the
   corners — furthest of all — go completely. */
.plan__stage::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 56% 78% at 50% 50%,
    transparent 0 42%,
    rgb(0 0 0 / 0.52) 72%,
    rgb(0 0 0 / 0.96) 100%);
  opacity: 0;
  transition: opacity 0.45s var(--ease-out);
  pointer-events: none;
}

.plan__stage.is-open::after {
  opacity: 1;
  /* late and quick: the geometry moves first and the rest of the plan
     goes quiet around the room once it has arrived */
  transition-delay: 0.35s;
}

.plan__drawing img {
  display: block;
  /* scaled by width, so --plan-span means the same thing for both models */
  width: calc(var(--plan-span, 1) * 100%);
  max-width: none;
  height: auto;
  max-height: 100%;
  object-fit: contain;
  /* Taken from the top-left, so a room is addressed as "move this corner
     of the drawing to that corner of the stage, then scale". */
  transform-origin: 0 0;
  transform:
    scale(var(--zoom, 1))
    translate(var(--zoom-x, 0%), var(--zoom-y, 0%));
  /* No easing on the transform. The change of room happens as a cut
     behind the dip below, not as a slide across the building — watching
     the drawing travel from one end to the other was the thing that read
     as the room arriving before the dip rather than out of it. It also
     leaves dragging free to follow the thumb exactly. */
}

/* Phones: the drawing keeps its own proportions, and the band at its foot
   is tighter because there is less box to fill. */
@media (max-width: 47.99rem) {
  .plan__drawing {
    --plan-foot: clamp(3.5rem, 15vw, 4.75rem);
    padding: clamp(0.9rem, 3.6vw, 1.35rem);
    padding-bottom: var(--plan-foot);
  }

  /* Still on its side, but at twice the size it would be drawn at the
     card's width (44px to the metre for the Studio at 375, not 22), and
     swiped along inside its sheet, fading at the sheet's edges as the
     picture rows do. The stage is padded at each end so the plan comes
     clear of the fade; the drawing is twice the stage with that padding
     put back. (Chosen from a study, tag plan-study, docs/studies/plan/.) */
  .plan__stage {
    aspect-ratio: auto;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    padding-inline: 0.75rem 2rem;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 0.75rem, #000 calc(100% - 2rem), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 0.75rem, #000 calc(100% - 2rem), transparent);
  }

  .plan__stage::-webkit-scrollbar {
    display: none;
  }

  .plan__drawing img {
    width: calc(var(--plan-span, 1) * 2 * (100% + 2.75rem));
    max-height: none;
  }
}

.plan__figure dt {
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: var(--fs-micro);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--c-ink-3);
}

.plan__figure dd {
  margin: var(--space-2) 0 0;
  font-family: var(--font-serif);
  font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2.125rem);
  line-height: 1;
  color: var(--c-ink);
  font-variant-numeric: tabular-nums;
}

.plan__unit {
  font-size: 0.5em;
  color: var(--c-ink-2);
}

/* A drawing annotation rather than a paragraph: small, sitting under the
   figures it qualifies. It carries the terms on the plans, so it is quiet
   but not faint: the register's small print, a size down. */
.plan__note {
  margin: var(--fine-gap) 0 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--c-ink-4);
}


/* =========================================================================
   Switching model

   The two models sit in the same grid cell, one on top of the other, so
   they can cross over rather than cut. The drawing dissolves and settles
   the last of a step sideways; the figures come up behind it in order,
   left to right, which reads as the numbers being re-set rather than
   replaced all at once.

   The block that is not on show is made inert by src/js/ui/variants.js, so
   nothing inside it can be tabbed to or read out while it is invisible.
   ========================================================================= */

.plan__models {
  display: grid;
}

.plan__model {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.45s var(--ease-out),
    visibility 0s linear 0.45s;
}

.plan__model.is-shown {
  opacity: 1;
  visibility: visible;
  transition:
    opacity 0.45s var(--ease-out),
    visibility 0s;
}

/* The drawing: a short slide in from the side it shares an edge with, so
   the movement runs along the building rather than across it. */
.plan__drawing img {
  translate: -1.25rem 0;
  transition:
    translate 0.7s var(--ease-out),
    opacity 0.45s var(--ease-out);
}

.plan__model.is-shown .plan__drawing img {
  translate: 0 0;
}

/* The figures: the same rise, stepped along the row. */
.plan__facts .plan__figure {
  translate: 0 0.5rem;
  opacity: 0;
  transition:
    translate 0.5s var(--ease-out),
    opacity 0.5s var(--ease-out);
}

.plan__model.is-shown .plan__facts .plan__figure {
  translate: 0 0;
  opacity: 1;
}

/* Stepped along the list, a pair at a time, so the two columns arrive
   together rather than chasing each other. */
.plan__model.is-shown .plan__figure:nth-of-type(-n + 2) { transition-delay: 0.10s; }
.plan__model.is-shown .plan__figure:nth-of-type(n + 3):nth-of-type(-n + 4) { transition-delay: 0.17s; }
.plan__model.is-shown .plan__figure:nth-of-type(n + 5) { transition-delay: 0.24s; }

/* Only the wide layout has anywhere to put it. */
.plan__blurb {
  display: none;
}

/* Nothing moves for anyone who would rather it did not; the change still
   happens, it simply happens at once. */
@media (prefers-reduced-motion: reduce) {
  .plan__model,
  .plan__drawing img,
  .plan__facts .plan__figure {
    transition-duration: 0.01ms;
    transition-delay: 0s;
    translate: none;
  }
}

/* ---- Tablet and up ----

   The same shape the wide layout has, one step down: the counts at the
   drawing's side and the areas in a band beneath it. The only thing the
   band cannot hold at this width is the enquiry — the prompt takes a
   fixed 241px and the three areas need about 130 each, which does not fit
   until 64rem — so it drops to a row of its own underneath.

   This used to be the old arrangement, all six figures stacked in the side
   column, which read as the section reverting rather than narrowing. */
@media (min-width: 48rem) {
  /* Three short figures do not need the width six did. */
  .plan {
    --plan-aside: clamp(10rem, 13vw, 13rem);
  }

  .plan__facts {
    display: contents;
  }

  /* The rule down the middle of the two-column list goes with the list.
     display: contents takes the element's own box away but not its
     pseudo-elements: left alone it survives as a grid item of the body,
     absolutely positioned against whatever it can find, and draws a
     hairline straight down the section. */
  .plan__facts::before {
    display: none;
  }

  .plan__body {
    grid-template-columns: repeat(3, minmax(0, 1fr)) var(--plan-aside);
    /* a flexible row, the three counts, the band, the enquiry */
    grid-template-rows: 1fr auto auto auto auto auto;
    column-gap: var(--plan-gap);
    row-gap: var(--space-3);
    align-items: center;
  }

  /* The drawing reads first, so it stays first in the markup for anyone
     not seeing the layout; on a wide screen it simply sits to the left. */
  .plan__drawing {
    grid-column: 1 / 4;
    grid-row: 1 / 5;
  }

  /* Down its side, sitting on its bottom edge. */
  .plan__figure--count { grid-column: 4; }
  .plan__figure--count:nth-of-type(1) { grid-row: 2; }
  .plan__figure--count:nth-of-type(2) { grid-row: 3; }
  .plan__figure--count:nth-of-type(3) { grid-row: 4; }

  .plan__figure--area {
    grid-row: 5;
    margin-top: clamp(var(--space-5), 3vw, var(--space-7));
  }

  .plan__figure--area:nth-of-type(4) { grid-column: 1; }
  .plan__figure--area:nth-of-type(5) { grid-column: 2; }
  .plan__figure--area:nth-of-type(6) { grid-column: 3; }

  .plan__figure { align-self: center; }

  /* The band reads across: the labels on one line, the figures on another
     under them, each label ruled off from its own. */
  .plan__figure--area {
    align-self: stretch;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }

  .plan__figure--area dt,
  .plan__figure--area dd {
    white-space: nowrap;
  }

  /* Room for the rule to sit in. Elsewhere the figure is a tight pair and
     8px is right; here a line goes between them, and at 8px it lands on
     the figure's ascenders. On the widest layout the row is tall enough
     that space-between holds them apart anyway, so this only shows where
     the band is compact. */
  .plan__figure--area dd {
    margin-top: var(--space-5);
  }

  .plan__figure--area dt {
    position: relative;
  }

  .plan__figure--area dt::after {
    content: "";
    position: absolute;
    top: calc(100% + var(--space-2));
    left: 0;
    width: max(70%, 9rem);
    height: 1px;
    background: var(--c-hairline);
  }

  /* Under the band until 64rem takes it into one. */
  .plan__body > .enquire {
    grid-column: 1 / -1;
    grid-row: 6;
    margin-top: var(--block-gap);
  }
}

/* ---- Desktop: the drawing takes the room ----

   The six figures stop being one block beside the drawing and become two
   groups: the counts stay at its side, spread down it, and the three areas
   drop into a row underneath with the enquiry as the last of the four.
   That hands the drawing the width the areas used to take and gives it the
   whole band to itself.

   display: contents on the list so its six children become items of this
   grid and can be placed individually. The list keeps its meaning — each
   figure is still a dt and dd inside it — it simply stops drawing a box of
   its own.

   The three area tracks can shrink — the drawing spans them, so a content
   floor on those tracks is a floor on the drawing, which then pushes the
   grid wider than the page. The labels are held on one line by nowrap
   instead: where a track is narrower than its label the text leans into
   the column gap beside it, which is wide enough to take it and has
   nothing in it. That keeps the band on one line at every width it runs
   at, without the drawing paying for it. */
@media (min-width: 64rem) {
  /* The side column carried six figures and now carries three short ones,
     so it gives the width back to the drawing. */
  .plan {
    --plan-aside: clamp(10rem, 13vw, 13rem);
  }

  .plan__body {
    /* The last of the four is sized by what is in it rather than by a
       share of the row: it holds a sentence, and the sentence has to stay
       on one line at every width. The three areas divide what is left,
       and they have to be able to shrink — the drawing spans them, so a
       max-content floor on those tracks becomes a floor on the drawing,
       which then pushes the whole grid wider than the page and drives the
       counts off the side of it. The width this layout starts at is what
       keeps the labels off two lines, not the track sizing. */
    grid-template-columns: repeat(3, minmax(0, 1fr)) max-content var(--plan-aside);
    /* Down the side of the drawing: a gap, the blurb, a bigger gap, then
       the three counts, then the band underneath everything. The drawing's
       spare height goes into the two flexible rows, split about four to
       six, which sets the blurb high on the drawing with room under it
       while the counts stay on its bottom edge. Moving that slack around
       is the whole of how this column is positioned — the counts used to
       divide it between them, which is what held them so far apart. */
    grid-template-rows: 1fr auto 1.4fr auto auto auto auto;
    row-gap: var(--space-3);
    align-items: center;
  }

  .plan__facts {
    display: contents;
  }

  .plan__drawing {
    grid-column: 1 / 5;
    grid-row: 1 / 7;
  }

  /* Down the side of the drawing: the blurb, then the three counts under
     it, each only as tall as it needs to be. The set of them sits at the
     foot of the drawing, the last count ending where the drawing does. */
  .plan__blurb {
    display: block;
    grid-column: 5;
    grid-row: 2;
    margin: 0 0 var(--space-4);
    font-size: var(--fs-small);
    line-height: 1.55;
    color: var(--c-ink-3);
    text-wrap: pretty;
  }

  .plan__figure--count { grid-column: 5; }
  .plan__figure--count:nth-of-type(1) { grid-row: 4; }
  .plan__figure--count:nth-of-type(2) { grid-row: 5; }
  .plan__figure--count:nth-of-type(3) { grid-row: 6; }

  /* The band under it all. The fourth place is the enquiry. The row gap is
     set tight for the counts above, so the band buys back the separation
     it wants with a margin of its own. */
  .plan__figure--area,
  .plan__body > .enquire {
    grid-row: 7;
    margin-top: clamp(var(--space-5), 3vw, var(--space-7));
  }

  .plan__figure--area:nth-of-type(4) { grid-column: 1; }
  .plan__figure--area:nth-of-type(5) { grid-column: 2; }
  .plan__figure--area:nth-of-type(6) { grid-column: 3; }

  .plan__figure { align-self: center; }

  /* The band is read across, not down: the labels make one line along the
     top of it and the figures another along the bottom, with the prompt
     and its button as the fourth of each. So the four take the height of
     the row and hold their two parts to its edges, rather than each being
     a tight pair sitting wherever its own height leaves it. */
  .plan__figure--area,
  .plan__body > .enquire {
    align-self: stretch;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }

  /* One line each, whatever the track comes out at. See the note above the
     block: the tracks cannot be given a content floor without the drawing
     inheriting it. */
  .plan__figure--area dt,
  .plan__figure--area dd {
    white-space: nowrap;
  }

  /* A rule under each label, short of the column it sits in so it reads as
     a mark under the words rather than as a border on a cell. Drawn by a
     pseudo-element out of the flow so that nothing above or below it
     moves: the label stays on the top line and the figure on the bottom
     one, exactly where the row puts them. The same hairline the section
     uses elsewhere. */
  .plan__figure--area dt {
    position: relative;
  }

  .plan__figure--area dt::after {
    content: "";
    position: absolute;
    top: calc(100% + var(--space-2));
    left: 0;
    /* Seven tenths of the column, but never less than the longest of the
       three labels, "Internal area", which sets at 129. On a narrow
       desktop the columns are tighter than that label and it leans into
       the gap beside it; a rule sized only off the column would then stop
       short of the word it belongs to. */
    width: max(70%, 9rem);
    height: 1px;
    background: var(--c-hairline);
  }

  /* The fourth place in the band. It takes the same top margin as the
     three areas beside it — set with them above — so the four still start
     on one line. */
  .plan__body > .enquire {
    grid-column: 4;
  }

  /* The prompt keeps the margin under it. It is what sets the height of
     the enquiry, and the enquiry is the tallest thing in the row, so it is
     what the row measures. Take it away and the row shrinks instead: the
     button rises to meet the numbers rather than the numbers dropping to
     meet the button. */

  /* One line, always. It is what the column is measured from, so it can
     never be the thing that has to give. */
  .plan__body > .enquire .enquire__prompt {
    white-space: nowrap;
  }

  /* And the button under it runs the full width of that sentence. */
  .plan__body > .enquire .enquire__link {
    width: 100%;
  }
}

/* =========================================================================
   The pieces in the band

   The same glass as everywhere else, set into the foot of the drawing:
   the room mix along the left, and the eye cut into the corner opposite.
   The eye is the peek piece from the two picture rows, turned through half
   a circle so its right angle lands in the bottom-right corner instead of
   the top-left.
   ========================================================================= */

/* Written with both classes so its cut outranks the plain one the chip
   rule sets: the two are otherwise evenly matched and the chip is further
   down the sheet. */
.plan__chip.plan__peek {
  position: absolute;
  z-index: 2;
  right: 0;
  bottom: 0;
  padding: 0;
  /* Its own outer corner is the bottom-right one, so it keeps the cut the
     way the buttons elsewhere on the page carry it. */
  --chip-line: var(--notch-line);
  --chip-corners: var(--notch-corners);
  --chip-clip: var(--notch-clip);
  cursor: pointer;
  transition: border-color 0.35s var(--ease-out), background 0.35s var(--ease-out);
}

@media (hover: hover) {
  .plan__peek:hover {
    --notch-edge: rgb(255 255 255 / 0.45);
    border-color: rgb(255 255 255 / 0.45);
    background: var(--chip-line), rgb(220 240 255 / 0.14);
  }
}

.plan__peek:focus-visible {
  outline: 2px solid var(--c-gold);
  outline-offset: 2px;
}

/* ---- The room mix ----
   Glass chips, not controls: the same three facts are set as proper
   labelled type in the strip below, so these are the glance version and
   are left out of the accessibility tree rather than read twice. */
.plan__mix {
  position: absolute;
  z-index: 2;
  left: 0;
  bottom: 0;
  display: flex;
  gap: var(--chip-gap);
  pointer-events: none;
}

/* The three read as one piece rather than three separate buttons, so only
   the group's outer corner is cut: the bedroom carries the chamfer at the
   bottom-left, where the row meets the corner of the cut-out, and the two
   behind it are left square. Each chip names its own cut through these
   three, which is what lets the states below set a background without
   having to know which chip they are dressing. */
.plan__chip {
  display: inline-grid;
  place-items: center;
  width: var(--chip);
  height: var(--chip);
  color: var(--c-white);
  --notch: 8px;
  --notch-edge: rgb(255 255 255 / 0.13);
  --chip-line: none;
  --chip-corners: 4px;
  --chip-clip: none;
  background: var(--chip-line), var(--glass-frost), rgb(8 12 16 / 0.2);
  border: 1px solid rgb(255 255 255 / 0.13);
  border-radius: var(--chip-corners);
  clip-path: var(--chip-clip);
  -webkit-backdrop-filter: var(--glass-filter);
  backdrop-filter: var(--glass-filter);
  box-shadow:
    inset 0 1px 1.5px rgb(255 255 255 / 0.16),
    inset 0 0 10px rgb(255 255 255 / 0.035);
}

.plan__chip[data-room="bed"] {
  --chip-line: var(--notch-line-bl);
  --chip-corners: var(--notch-corners-bl);
  --chip-clip: var(--notch-clip-bl);
}

.plan__pupil {
  fill: currentColor;
  stroke: none;
}

.plan__chip svg {
  display: block;
  width: 0.95rem;
  height: 0.95rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.85;
}

/* =========================================================================
   Going in close on a room

   Each room is three numbers on the stage: how far in, and how far the
   drawing has to move to bring that room to the corner. They are set per
   model, because the two buildings are laid out back to front from one
   another — SOLUM reads living, bathroom, bedroom from the left, the
   Studio reads bedroom, bathroom, living.

   Percentages are of the drawing's own size, which is why they survive
   every screen width: the stage changes size, the fractions do not.
   ========================================================================= */

/* Each room framed whole in the grown box.

   The numbers come out of the geometry rather than off the eye. A room
   occupies a known slice of the drawing; the scale is whichever of the
   two fits — the stage's width over the room's width, or its height over
   the room's height — and the offsets then centre what is left. The
   narrow rooms come out height-limited at 2.25 and sit with black either
   side; the living end is width-limited at 1.87 and fills the frame.
   ========================================================================= */

.plan__model[data-variant="standard"] .plan__stage[data-room="living"] img {
  --zoom: 1.873; --zoom-x: -1.2%; --zoom-y: 6.7%;
}
/* The bathroom is the exception: it does not run the full depth of the
   building the way the other two do, so the height stops being what
   limits it and the width takes over. It carries more magnification than
   the rest. */
.plan__model[data-variant="standard"] .plan__stage[data-room="bath"] img {
  --zoom: 3.4; --zoom-x: -49.8%; --zoom-y: -1%;
}
.plan__model[data-variant="standard"] .plan__stage[data-room="bed"] img {
  --zoom: 2.25; --zoom-x: -64.6%; --zoom-y: -2.8%;
}

.plan__model[data-variant="studio"] .plan__stage[data-room="bed"] img {
  --zoom: 2.59; --zoom-x: 4.4%; --zoom-y: -8%;
}
.plan__model[data-variant="studio"] .plan__stage[data-room="bath"] img {
  --zoom: 2.59; --zoom-x: -20.6%; --zoom-y: -8%;
}
.plan__model[data-variant="studio"] .plan__stage[data-room="living"] img {
  --zoom: 2.47; --zoom-x: -52%; --zoom-y: -6%;
}

/* A room being looked at shows in its chip, the same way the swatches
   elsewhere show which finish is on. */
.plan__room[aria-pressed="true"] {
  --notch-edge: var(--c-gold);
  color: var(--c-gold);
  border-color: var(--c-gold);
  background: var(--chip-line), rgb(214 179 106 / 0.16);
}

/* ---- What is being looked at ---- */
.plan__closeup {
  position: absolute;
  z-index: 2;
  right: calc(var(--plan-eye-w) + var(--space-3));
  bottom: 0;
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  height: var(--chip);
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  white-space: nowrap;
  color: rgb(0 0 0 / 0.62);
  opacity: 0;
  transition: opacity 0.45s var(--ease-out);
}

.plan__stage.is-open ~ .plan__closeup {
  opacity: 1;
}

.plan__closeup-size {
  font-family: var(--font-serif);
  font-size: 0.9375rem;
  letter-spacing: 0;
  text-transform: none;
  color: rgb(0 0 0 / 0.82);
}

/* Phones have no room for both: the size carries the meaning. */
@media (max-width: 30rem) {
  .plan__closeup-name {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .plan__drawing img {
    transition-duration: 0.01ms;
  }
}

/* The room chips are controls now, so they take the pointer back. */
.plan__mix {
  pointer-events: auto;
}

.plan__room {
  cursor: pointer;
  transition:
    color 0.35s var(--ease-out),
    border-color 0.35s var(--ease-out),
    background 0.35s var(--ease-out);
}

@media (hover: hover) {
  .plan__room:not([aria-pressed="true"]):hover {
    --notch-edge: rgb(255 255 255 / 0.45);
    border-color: rgb(255 255 255 / 0.45);
    background: var(--chip-line), rgb(220 240 255 / 0.14);
  }
}

/* Switched off for now while the close-ups are refined: in place, a
   little quieter, and not answering the pointer. */
.plan__room:disabled {
  cursor: default;
  opacity: 0.55;
}

@media (hover: hover) {
  .plan__room:disabled:hover {
    --notch-edge: rgb(255 255 255 / 0.22);
    border-color: rgb(255 255 255 / 0.22);
    background: var(--chip-line), rgb(8 12 16 / 0.32);
  }
}

.plan__room:focus-visible {
  outline: 2px solid var(--c-gold);
  outline-offset: 2px;
}

/* ---- Dragging the plan about ----
   Only once a room is open: at the full plan there is nothing to move to.
   pan-y leaves the page's own vertical scrolling alone, so a thumb drawn
   up the screen still scrolls and one drawn across moves the drawing. */
.plan__stage.is-open {
  cursor: grab;
  touch-action: pan-y;
}

.plan__stage.is-open.is-dragging {
  cursor: grabbing;
}

/* The drawing has to keep up with the thumb, not ease after it. */
.plan__stage.is-dragging img {
  transition: none;
}

.plan__stage img {
  user-select: none;
  -webkit-user-drag: none;
}

/* ---- The move itself ----

   Going from one room straight to another slides the drawing the whole
   way across the building, which is a lot of travel to watch. The drawing
   dips as it goes and comes back up on arrival, so the movement reads as
   a change of subject rather than a journey between two of them. Opening
   from the full plan gets the same dip, which is what keeps the two
   cases feeling like one gesture.

   An animation rather than a transition, because a transition cannot go
   down and come back up again on one property. */
@keyframes plan-settle {
  0%   { opacity: 1; }
  18%  { opacity: 0.48; }
  32%  { opacity: 0.05; }
  42%  { opacity: 0.05; }   /* the cut happens in here */
  62%  { opacity: 0.5; }
  80%  { opacity: 0.86; }
  100% { opacity: 1; }
}

/* Linear, with the shape written into the stops above: an easing curve on
   top of them would bend the hold at the bottom and the cut could show. */
.plan__stage.is-settling img {
  animation: plan-settle var(--plan-move) linear;
}

/* The caption swaps its words behind the dip rather than in front of it. */
.plan__closeup {
  translate: 0 0.25rem;
  transition:
    opacity 0.4s var(--ease-out),
    translate 0.4s var(--ease-out);
}

.plan__stage.is-open ~ .plan__closeup {
  translate: 0 0;
  transition-delay: 0.42s;
}

/* Matched through the stage so it can outweigh the rule that holds the
   caption up while a room is open — that one carries a class, an
   attribute and a class, and a bare .plan__closeup.is-swapping loses to
   it and never fades. */
.plan__stage ~ .plan__closeup.is-swapping {
  opacity: 0;
  transition-duration: 0.18s;
  transition-delay: 0s;
}

@media (prefers-reduced-motion: reduce) {
  .plan__stage,
  .plan__stage::after,
  .plan__closeup {
    transition-duration: 0.01ms;
    transition-delay: 0s;
  }

  .plan__stage.is-settling img {
    animation: none;
  }
}

/* (The eye is never idle now: with no room open it opens the plan full
   screen, ui/plan-viewer.js, so it no longer softens to say it has
   nothing to do.) */

/* =========================================================================
   A shorter screen

   Where the screen is not tall enough for the plan's stage at the page's
   full width, the air around the drawing is what gives, not the drawing:
   the gap under the head, the band under the drawing and the note under
   that close up, so the floor plan keeps the same edges as every other
   section and still fits the screen whole. Tall screens keep their air.
   ========================================================================= */
@media (min-width: 56rem) and (max-height: 56rem) {
  .plan.plan--card > :first-child {
    margin-top: calc(var(--card-top) + 3.2cqw);
  }

  .plan .section-head,
  .has-scene .scene--plan .section-head {
    margin-bottom: min(var(--head-gap), 4.5vh);
  }

  .plan__figure--area,
  .plan__body > .enquire {
    margin-top: var(--space-5);
  }

  .plan__note {
    margin-top: var(--space-4);
  }
}

/* =========================================================================
   The plan on its own (ui/plan-viewer.js)

   Opened from the eye: the plan full screen on the page's black, still on
   its side, the page's glass for its three buttons.
   ========================================================================= */
.plan-viewer {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  color: var(--c-ink);
  background: rgb(0 0 0 / 0.94);
}

.plan-viewer::backdrop {
  background: rgb(0 0 0 / 0.6);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.plan-viewer__pane {
  position: absolute;
  inset: 0;
  display: flex;
  overflow: auto;
  overscroll-behavior: contain;
  padding:
    calc(4.5rem + var(--safe-top))
    calc(var(--gutter) + var(--safe-right))
    calc(4.5rem + var(--safe-bottom))
    calc(var(--gutter) + var(--safe-left));
}

.plan-viewer__pane.is-zoomed {
  cursor: grab;
}

/* centred while it fits, scrolled along once it does not (auto margins
   give way to nothing when the plan outgrows the pane) */
.plan-viewer__img {
  display: block;
  flex: none;
  width: 100%;
  max-width: none;
  height: auto;
  margin: auto;
}

.plan-viewer__btn {
  display: inline-grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  color: var(--c-ink);
  border: 1px solid rgb(255 255 255 / 0.14);
  border-radius: 50%;
  background: var(--glass-frost), rgb(8 12 16 / 0.4);
  -webkit-backdrop-filter: var(--glass-filter);
  backdrop-filter: var(--glass-filter);
  box-shadow: var(--glass-edge);
  cursor: pointer;
}

.plan-viewer__btn:disabled {
  opacity: 0.35;
  cursor: default;
}

.plan-viewer__btn:focus-visible {
  outline: 2px solid var(--c-gold);
  outline-offset: 2px;
}

.plan-viewer__btn svg {
  width: 1.1rem;
  height: 1.1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
}

.plan-viewer__close {
  position: absolute;
  top: calc(var(--space-5) + var(--safe-top));
  right: calc(var(--gutter) + var(--safe-right));
}

.plan-viewer__zoom {
  position: absolute;
  left: 50%;
  bottom: calc(var(--space-5) + var(--safe-bottom));
  display: flex;
  gap: var(--space-3);
  translate: -50% 0;
}
