@charset "UTF-8";
.card-grid-hairline {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(255px, 1fr));
  gap: 2px;
}
.card-grid-hairline > * {
  background: var(--bg);
  box-shadow: 0 0 0 1px var(--border-color);
}
.section .elementor-widget-heading.price-meta, .price-meta {
  font-family: var(--font-heading, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
  font-weight: 700;
  font-size: 1.1875rem;
  line-height: 1.2;
  color: var(--accent);
  margin-top: auto;
  padding-top: 0.875rem;
}
.section .elementor-widget-heading.price-meta .elementor-heading-title, .price-meta .elementor-heading-title {
  color: inherit;
  font: inherit;
}
.accent-rule-point {
  border-left: 3px solid var(--accent);
  padding-left: 1.125rem;
}
a.btn-outline-ink, button.btn-outline-ink, .elementor-widget-button.btn-outline-ink .elementor-button, .elementor-widget-button.btn-outline-ink a.elementor-button {
  background: none;
  border: 2px solid var(--text-color);
  color: var(--text-color);
}
a.btn-outline-ink:hover, a.btn-outline-ink:focus, button.btn-outline-ink:hover, .elementor-widget-button.btn-outline-ink a.elementor-button:hover, .elementor-widget-button.btn-outline-ink a.elementor-button:focus {
  background: var(--text-color);
  border-color: var(--text-color);
  color: var(--bg);
}
.head-condensed-uc, .head-condensed-uc .elementor-heading-title {
  text-transform: uppercase;
  letter-spacing: 0.01em;
}
a.btn-solid-invert, button.btn-solid-invert, .elementor-widget-button.btn-solid-invert .elementor-button, .elementor-widget-button.btn-solid-invert a.elementor-button {
  background: var(--text-color);
  border: none;
  color: var(--bg);
}
a.btn-solid-invert:hover, a.btn-solid-invert:focus, .elementor-widget-button.btn-solid-invert a.elementor-button:hover, .elementor-widget-button.btn-solid-invert a.elementor-button:focus {
  background: var(--text-color);
  color: var(--bg);
  opacity: 0.88;
}
/* ═══════════════════════════════════════════════════════════════════════════════════════════
   AUDIT DEVICES — added 2026-09-18 for the seven sections the wireframe audit whitelisted
   (b-hero-18/19/20 · b-services-12 · b-testimonial-11 · b-gallery-13 · b-howitworks-12).

   🔴 EVERY COLOUR IS A TOKEN. The only literals are geometry — rotation degrees, radii, rule
   widths, grid minimums — and each is measured off its wireframe rather than chosen.

   🪤 ALPHA IS DONE WITH color-mix(), NOT rgba(var(--x-rgb)). BPS Lite ships exactly one `-rgb`
   twin, `--accent-rgb`. There is no `--text-color-rgb` and no `--accent-2-rgb` — those are
   Base's. Every planned `rgba(var(--text-color-rgb), .14)` would have painted NOTHING, with no
   error, which is trap 2 of the seven. Verified: scssphp passes color-mix through unchanged.

   🪤 A CHIP LANDS ON THE WIDGET WRAPPER, never on the inner tag. `.label-mono` is on
   `.elementor-element.elementor-widget-heading`; the <h3> inside carries only
   `.elementor-heading-title`. So a COMPOUND selector matches nothing and every typography rule
   below is a DESCENDANT selector. That is trap 5, and it shipped the last batch's prices in the
   wrong case before it was found.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
/* ---- hairlines -------------------------------------------------------------------------- */
/* An alpha of the ink, not a fixed grey, so the rule rides whatever ground the band sits on.
   Every row carries one INCLUDING THE LAST, so a list closes with a line. */
.row-rule {
  border-bottom: 1px solid color-mix(in srgb, var(--text-color) 14%, transparent);
  padding-bottom: 0.9rem;
}
.row-rule-top {
  border-top: 1px solid color-mix(in srgb, var(--text-color) 14%, transparent);
  padding-top: 1.2rem;
}
/* ---- panel ------------------------------------------------------------------------------ */
/* The bordered box behind a status table or a result card. Its own ground, one hairline, no
   radius and no shadow — the wireframes are flat here and a shadow makes it read as a popup. */
.panel-hairline {
  border: 1px solid var(--border-color);
  background: var(--bg);
}
.panel-hairline > .e-con-inner > .row-rule:last-of-type, .panel-hairline > .row-rule:last-of-type {
  border-bottom: 0;
}
/* ---- cards ------------------------------------------------------------------------------ */
/* 🪤 Separation comes from the BORDER, never from a tone — on several palettes the two light
   rungs are the same hex and a tone-separated card is invisible by construction. */
.card-soft {
  border: 1px solid var(--border-color);
  border-radius: 18px;
  overflow: hidden;
}
/* An Elementor image widget centres its <img> at intrinsic width, so a card wrapped round one
   drew its rounded border out at the container edge while the picture sat inset inside it —
   the frame and its contents visibly disagreeing. */
.card-soft img {
  display: block;
  width: 100%;
}
/* ---- grids ------------------------------------------------------------------------------ */
/* Where a row is really a grid, it is a grid. Elementor percentage widths plus a flex gap
   overflow (52% + 44% + 54px does not fit 1140px) and the overflow reads as a deliberate
   stacked layout rather than a break. auto-fit needs no width maths at all. */
.card-grid-auto {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
}
.card-grid-2up {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 10px;
}
/* 🪤 A REPEATING ROW MUST DIVIDE EVENLY (design-manners rule 8). Measured: six cards in
   `card-grid-auto` at 1140px fit FOUR per row, so the grid came out 4 + 2 and the short second
   row read as two cards that failed to load rather than as a deliberate stop. An explicit
   three-up divides six evenly and still collapses to 2 then 1 as the viewport narrows. */
/* 🪤 `auto-fit, minmax(260px, 1fr)` IS NOT A THREE-UP. At 1140px four 260px tracks plus three
   16px gaps come to 1088px, so auto-fit fits FOUR and six cards still came out 4 + 2. auto-fit
   answers "how many fit", which is the wrong question when the count has to divide. An explicit
   three-up with its own breakpoints is the only way to guarantee the division. */
.card-grid-3up {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
@media (max-width: 1024px) {
  .card-grid-3up {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 640px) {
  .card-grid-3up {
    grid-template-columns: 1fr;
  }
}
.card-grid-auto > *, .card-grid-2up > *, .card-grid-3up > * {
  min-width: 0;
}
/* A stat trio is a three-up too — as a flex row with no widths each stat took the full column
   and the three stacked, which reads as a list rather than as a set. */
.stat-3up {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
@media (max-width: 640px) {
  .stat-3up {
    grid-template-columns: 1fr 1fr;
  }
}
.stat-3up > * {
  min-width: 0;
}
/* A pill in a stretch-aligned column inherits the column's full width, so its border ran the
   whole measure and it stopped reading as a pill at all. */
.self-start {
  align-self: flex-start;
  width: auto;
}
/* ---- the picker row (static state) ------------------------------------------------------ */
/* 🛑 The selected state is PAINTED, not behavioural. The wireframe swaps it in JS; that is the
   widget track. The section has to look right before behaviour is added to it. */
.pick-row, .pick-row-on {
  border: 1.5px solid var(--border-color);
  background: var(--bg);
  padding: 0.85rem 1.1rem;
}
.pick-row-on {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 9%, var(--bg));
}
/* ---- value / label typography ----------------------------------------------------------- */
/* The right-hand value in a status row, a stat, or a platform score. Deliberately NOT
   `price-meta`: that one carries `margin-top:auto` + padding to pin itself to the foot of a
   card, which is wrong inside a row. And deliberately not `stat-number`: 32-42px and
   `white-space:nowrap`, so more than two words overflows silently. */
/* 🪤 SPECIFICITY, MEASURED. `.value-accent .elementor-heading-title` is (0,2,0) and the skin's
   own heading rule is (0,3,0), so the transform and the size applied and THE COLOUR DID NOT —
   the value rendered in ink while every other declaration in the same block worked. A partial
   win is far worse than a clean miss: it looks deliberate. The shipped `price-meta` already
   solved this with a `.section` prefix; these match it rather than inventing a second answer. */
.section .elementor-widget-heading.value-accent .elementor-heading-title, .section .value-accent .elementor-heading-title, .value-accent, .value-accent .elementor-heading-title {
  color: var(--accent);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.375rem;
  line-height: 1.15;
}
.section .elementor-widget-heading.label-mono .elementor-heading-title, .section .label-mono .elementor-heading-title, .label-mono, .label-mono .elementor-heading-title {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* 🪤 NOT `--text-color3`. setup.php's own note records that this skin corrected --light-text3
     to #7d6b5e for 4.92:1 — but that is measured ON --light-bg. On the light2 and light3 rungs
     the ground is darker and the same token falls to 3.9:1, under AA, at the 11px this label
     runs at. A tertiary token is only tertiary against the rung it was ramped for. */
  color: var(--text-color2);
}
/* A label|value pair is a GRID, not a flex row. Measured: as flex, the text column took 405px
   beside a 55px value inside a 455px panel — 460px total — so every row wrapped its value onto
   a second line. `1fr auto` cannot do that at any width, and needs no width maths. */
.pair-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 16px;
  align-items: baseline;
}
.pair-row > * {
  min-width: 0;
}
/* ---- pills ------------------------------------------------------------------------------ */
/* 🪤 gate.py's narrow-text check flags every one of these (27-67px measured). A pill is narrow
   by design — the same false positive already logged against b-intro-11's 24 pills. */
.chip-pill, .chip-pill-on, .chip-pill-sm {
  display: inline-block;
  border-radius: 999px;
  border: 1px solid var(--border-color);
  background: var(--bg);
  padding: 0.65rem 1.1rem;
  line-height: 1;
}
.chip-pill-sm {
  padding: 0.35rem 0.7rem;
  font-size: 0.78rem;
}
.section .chip-pill-sm .elementor-heading-title, .chip-pill-sm, .chip-pill-sm .elementor-heading-title {
  color: var(--text-color2);
}
.chip-pill-on {
  background: var(--text-color);
  border-color: var(--text-color);
}
.section .chip-pill-on .elementor-heading-title, .chip-pill-on, .chip-pill-on .elementor-heading-title {
  color: var(--bg);
}
/* ---- media ------------------------------------------------------------------------------ */
.img-square img {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
}
/* The scrim + overlaid label. One relative box: the image fills it, ::after paints the fade,
   the heading sits over both. Text on the scrim takes --dark-text because the scrim is dark on
   every palette by construction. */
.img-scrim-bottom {
  position: relative;
  overflow: hidden;
}
.img-scrim-bottom img {
  display: block;
  width: 100%;
  aspect-ratio: 1.6;
  object-fit: cover;
}
.img-scrim-bottom::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, transparent 45%, color-mix(in srgb, var(--dark-bg) 82%, transparent) 100%);
}
.img-scrim-bottom > .elementor-widget-heading {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 1rem 1.15rem;
}
.section .img-scrim-bottom > .elementor-widget-heading .elementor-heading-title, .img-scrim-bottom > .elementor-widget-heading .elementor-heading-title {
  color: var(--dark-text);
}
/* ---- the collage ------------------------------------------------------------------------ */
/* Three absolutely placed cards inside one relative box, rotated -3 / +4 / -2 degrees. The
   rotations ALTERNATE and the third card is landscape while the others are portrait — that
   asymmetry is the device. Reproducing it evenly kills it. Shadows are large-offset with a
   heavily negative spread, which is what lifts the cards off the ground rather than smudging
   them. Degrees, ratios and radii are the wireframe's own numbers. */
.collage-stack {
  position: relative;
  aspect-ratio: 1.0204081633;
  min-width: 0;
}
.collage-stack > .elementor-widget-image {
  position: absolute;
  margin: 0;
}
.collage-stack > .elementor-widget-image img {
  display: block;
  width: 100%;
  object-fit: cover;
}
.collage-tilt-a {
  left: 0;
  top: 5%;
  width: 52%;
  transform: rotate(-3deg);
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 26px 50px -22px color-mix(in srgb, var(--text-color) 42%, transparent);
}
.collage-tilt-b {
  right: 0;
  top: 0;
  width: 45%;
  transform: rotate(4deg);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 20px 40px -20px color-mix(in srgb, var(--text-color) 40%, transparent);
}
.collage-tilt-c {
  right: 0;
  bottom: 0;
  width: 44%;
  transform: rotate(-2deg);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 22px 44px -20px color-mix(in srgb, var(--text-color) 42%, transparent);
}
.collage-tilt-a img, .collage-tilt-b img {
  aspect-ratio: 0.8;
}
.collage-tilt-c img {
  aspect-ratio: 1.25;
}
/* ---- ambient bloom ---------------------------------------------------------------------- */
/* The entire device is one declaration: two off-canvas radial blooms at very low alpha over the
   band's own ground. One origin sits ABOVE the top edge (-10%), which is why it reads as light
   entering the frame rather than as a decorative blob inside it. */
.ambient-bloom {
  background: radial-gradient(circle at 20% -10%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 55%), radial-gradient(circle at 90% 10%, color-mix(in srgb, var(--accent-2) 14%, transparent), transparent 50%), var(--bg);
}
/* ---- width wells ------------------------------------------------------------------------ */
/* The second half of the gradient hero is the WIDTH STEP: copy constrained narrow, media
   breaking out wider inside the same tint. Losing the step loses the effect even with the
   gradient intact. */
.well-narrow {
  max-width: 840px;
  margin-inline: auto;
  width: 100%;
}
.well-wide {
  max-width: 1080px;
  margin-inline: auto;
  width: 100%;
}
/* ---- editorial day rows ----------------------------------------------------------------- */
.day-grid {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 24px;
  align-items: start;
}
.day-grid > * {
  min-width: 0;
}
.section .elementor-widget-heading.day-label .elementor-heading-title, .section .day-label .elementor-heading-title, .day-label, .day-label .elementor-heading-title {
  font-family: var(--font-heading);
  font-size: 1.25rem;
  font-weight: 400;
  /* 🪤 Bare `--accent-2` measured 2.95:1 here. A second accent is chosen to sit BESIDE the first,
     not to carry 20px text on a near-white ground, and at 400 weight under 24px this needs the
     full 4.5. Mixed toward the ink it keeps the sage identity and clears AA — and because both
     sides are tokens it re-derives correctly on any palette. */
  color: color-mix(in srgb, var(--accent-2) 62%, var(--text-color));
}
/* ---- timestamped itinerary (b-howitworks-13) -------------------------------------------- */
/* A fork of `day-grid` with a RAIL in the gutter: one bead per row, joined by a hairline.
   🔴 THE RAIL IS DRAWN ON THE ROW, NOT IN A CELL OF ITS OWN. A third grid cell holding the
   dot is the obvious construction and it is the wrong one — an empty Elementor container
   collapses to zero height, so the connector would have nothing to run down. Both marks are
   pseudo-elements on the row itself, which is the G6 shape (the glaze device that needed no
   markup at all, and the one its own plan rated best in the set).
   🪤 104px IS NOT A FREE NUMBER. It is the centre of the gutter that `88px 1fr` + `gap: 32px`
   produces (88 → 120). Change either and the beads leave the line. */
.time-grid {
  position: relative;
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: 32px;
  align-items: start;
  padding-bottom: 30px;
}
.time-grid > * {
  min-width: 0;
}
.time-grid::before {
  content: "";
  position: absolute;
  left: 104px;
  top: 6px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  transform: translateX(-50%);
  background: var(--accent);
  /* The ring is the band's own ground, so the bead reads as sitting ON the line rather than
     beside it — and it re-derives on any tone instead of needing a per-skin value. */
  box-shadow: 0 0 0 4px var(--bg);
}
.time-grid::after {
  content: "";
  position: absolute;
  left: 104px;
  top: 21px;
  bottom: 0;
  width: 1px;
  transform: translateX(-50%);
  /* Same alpha-of-ink as `row-rule`, for the same reason: it rides whatever ground it lands on
     rather than being a fixed grey that only works on one of the three skins. */
  background: color-mix(in srgb, var(--text-color) 14%, transparent);
}
/* 🔴 The LAST row kills its connector, and this is the one place the device deliberately parts
   company with `row-rule` — which carries its rule on EVERY row including the last, so the list
   closes with a line. A rail is the opposite: a line continuing past the final bead reads as a
   list that got cut off. */
.time-grid-last {
  padding-bottom: 0;
}
.time-grid-last::after {
  content: none;
}
/* The media half of a split itinerary. Without this the image sits at its natural height at the
   TOP of its column and the rest of that column is blank — which is the same "half the band is
   missing" read the empty slot produces, just moved down the page. Needs the ROW to stretch its
   children (the section ships `flex_align_items: stretch` for this reason) and a definite height
   to fill, which the taller copy column supplies. */
.img-fill-col {
  flex: 1 1 auto;
  min-height: 0;
}
.img-fill-col img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.section .time-label .elementor-heading-title, .time-label, .time-label .elementor-heading-title {
  font-family: var(--font-heading);
  font-size: 0.95rem;
  font-weight: 600;
  /* Tabular figures so 7:00 and 11:30 align on the colon down the column. Without this the
     column reads as ragged even though every row is set identically. */
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  color: color-mix(in srgb, var(--accent-2) 62%, var(--text-color));
}
/* ---- quadrant framework wheel (b-framework-01) ------------------------------------------ */
/* 🔴 CONIC IS THE ONLY WAY IN. Elementor 4.2.4's gradient control offers linear and radial
   only, so a four-quadrant ring cannot be reached from any native setting — which is the whole
   reason this is a chip rather than a section setting.
   🪤 `e-no-lazyload` IS REQUIRED ON THE CONTAINER CARRYING `.wheel`, and it is not decoration.
   Elementor ships an inline !important rule nulling `background-image` on
   `.e-con.e-parent:nth-of-type(n+4)` until it marks the element lazy-loaded, and a background
   arriving from a class it does not know about never qualifies — so past the third top-level
   container the wheel silently disappears. The section JSON carries the class for this reason. */
.wheel {
  position: relative;
  width: 100%;
  max-width: 360px;
  aspect-ratio: 1;
  margin-inline: auto;
  border-radius: 50%;
  /* Four tints of ONE hue, not four colours: the quadrants must read as parts of one system,
     and a palette only guarantees two accents. Every stop is a token mix, so the wheel
     re-derives on any skin instead of being tied to the palette it was drawn against. */
  background: conic-gradient(var(--accent) 0deg 90deg, color-mix(in srgb, var(--accent) 55%, var(--bg)) 90deg 180deg, color-mix(in srgb, var(--accent) 78%, var(--text-color)) 180deg 270deg, color-mix(in srgb, var(--accent) 30%, var(--bg)) 270deg 360deg);
}
/* The cross, and the hub under it, are painted in the BAND'S OWN GROUND so they read as the
   page showing through rather than as a third colour laid on top.
   🔑 `var(--bg)` is the correct token here precisely BECAUSE it resolves to whatever tone the
   section carries. The standing warning against `--bg` is about reaching for it as a DISTINCT
   colour, which this is not. Do not "fix" these to a literal. */
.wheel::before, .wheel::after {
  content: "";
  position: absolute;
  background: var(--bg);
}
.wheel::before {
  left: 50%;
  top: 0;
  bottom: 0;
  width: 3px;
  transform: translateX(-50%);
}
.wheel::after {
  top: 50%;
  left: 0;
  right: 0;
  height: 3px;
  transform: translateY(-50%);
}
/* 🪤 `z-index` IS LOAD-BEARING AND THE DEFECT IT PREVENTS IS INVISIBLE TO EVERY GATE. The cross
   is `.wheel::after`, which a browser paints as the LAST child of `.wheel` — so with both it and
   the hub at `z-index: auto`, the bar paints straight through the hub and strikes out the label
   in the middle of it. Measured 2026-09-18 by zooming into the rendered wheel: MP-04 was green,
   every computed style was correct, and the centre of the device read as struck-through text. */
.wheel-hub {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  width: 46%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 6%;
}
/* The frame: four labels at the corners of a square holding the wheel. REAL GRID CELLS, not
   absolute positioning — the corners have to survive 390px, where they stack under the wheel
   instead of piling on top of it. Each corner carries its own chip rather than being placed by
   `nth-child`, so a mould can drop one label without the other three moving. */
/* 🔴 THE CENTRE TRACK IS DEFINITE, AND `auto` IS NOT A SAFE DEFAULT HERE. `auto` sizes the
   track from its content, and the content is `.wheel` at `width: 100%` of that same track — a
   circular dependency that resolves to the hub label's min-content width. Measured 2026-09-18:
   the track came out 40px and the wheel painted at 20×20, with the conic gradient applied
   correctly the whole time. Nothing reported it: the chip was registered, the CSS compiled, the
   background computed, and MP-04 is green on a page whose wheel is the size of a full stop. */
.wheel-frame {
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(240px, 30%, 360px) minmax(0, 1fr);
  grid-template-rows: auto auto;
  gap: 32px 40px;
  align-items: center;
}
.wheel-frame > * {
  min-width: 0;
}
/* 🪤 `text-align` on these cells is a FALLBACK, not the mechanism. Elementor writes the
   heading widget's own `align` setting as a rule on the widget, which beats anything inherited
   from the cell — so the corner labels set their alignment in their own settings (level 2 of
   the ladder) and these declarations only catch the text-editor, which has no align of its own
   unless one is passed. Measured: with the cells alone, all four corners painted left. */
/* 🪤 THE CORNER CELLS ARE CAPPED AND PUSHED TOWARD THE WHEEL. Left at the full 1fr track they
   are ~360px wide, so a two-line label sets as a wide ragged block sitting a long way from the
   thing it labels, and the four of them stop reading as corners of one diagram. Capped and
   pulled inward they sit against the ring, which is what makes the arrangement legible as a
   wheel rather than as a four-column list with a picture in the middle. */
.wheel-nw, .wheel-ne, .wheel-sw, .wheel-se {
  max-width: 250px;
}
.wheel-nw {
  grid-column: 1;
  grid-row: 1;
  text-align: right;
  margin-left: auto;
}
.wheel-ne {
  grid-column: 3;
  grid-row: 1;
  text-align: left;
  margin-right: auto;
}
.wheel-mid {
  grid-column: 2;
  grid-row: 1 / span 2;
  width: 100%;
}
.wheel-sw {
  grid-column: 1;
  grid-row: 2;
  text-align: right;
  margin-left: auto;
}
.wheel-se {
  grid-column: 3;
  grid-row: 2;
  text-align: left;
  margin-right: auto;
}
@media (max-width: 767px) {
  /* The rail has nothing to run down once the time sits above its own row, so it goes entirely
     rather than dangling in the margin beside a stacked list. */
  /* One column, wheel first: source order is NW · NE · wheel · SW · SE, which would read as two
     orphaned labels before the thing they label. Rows are assigned explicitly rather than left
     to auto-placement, which would put NW back alongside the wheel. */
  /* 🪤 SE BEFORE SW, and the swap is the point. The corners are addressed by POSITION
     (NW·NE·SW·SE) while the numerals run CLOCKWISE (01·02·03·04 = NW·NE·SE·SW). Around a ring
     both readings are correct at once; stacked in a column only one can be, and the one that
     matters is the numbering the reader is following — left in source order the list counts
     01, 02, 04, 03. */
  /* Stacked, the media column has no sibling to take its height from, so it would collapse to
     the image's own ratio inside a `height: 100%` that resolves to auto. Give it a shape. */
  .day-grid {
    grid-template-columns: 1fr;
    gap: 6px;
  }
  .collage-stack {
    aspect-ratio: 0.9090909091;
  }
  .time-grid {
    grid-template-columns: 1fr;
    gap: 4px;
    padding-bottom: 22px;
  }
  .time-grid::before, .time-grid::after {
    content: none;
  }
  .wheel-frame {
    grid-template-columns: 1fr;
    gap: 18px;
  }
  .wheel-mid {
    grid-column: 1;
    grid-row: 1;
  }
  .wheel-nw, .wheel-ne, .wheel-sw, .wheel-se {
    grid-column: 1;
    text-align: left;
    max-width: none;
    margin-left: 0;
    margin-right: 0;
  }
  .wheel-nw {
    grid-row: 2;
  }
  .wheel-ne {
    grid-row: 3;
  }
  .wheel-se {
    grid-row: 4;
  }
  .wheel-sw {
    grid-row: 5;
  }
  .img-fill-col {
    flex: 0 0 auto;
  }
  .img-fill-col img {
    height: auto;
    aspect-ratio: 1.3333333333;
  }
}
