/* ══════════════════════════════════════════════════════════════════════════
   FERTILITY WONDERLAND — ROOM STYLESHEET
   ONE layout system for every room. Three colours. One type scale.
   ONE vertical rhythm. ONE pill. ONE button. ONE card grid. ONE centre axis.
   Nothing on a room page is positioned by hand. There are no per-element
   margins anywhere in this file — that is the whole point.
   ══════════════════════════════════════════════════════════════════════════ */

:root{
  /* ── THE THREE COLOURS ────────────────────────────────────────────── */
  --pink:#FF3EAF;
  --white:#fff;
  --ink:#000;

  --card:#fff;
  --glass:rgba(255,255,255,.88);
  --edge:rgba(255,62,175,.22);
  --edge-hi:rgba(255,62,175,.55);
  --band:rgba(255,62,175,.32);
  --lift:0 20px 52px rgba(0,0,0,.18);
  --lift-hi:0 30px 72px rgba(0,0,0,.26);
  --lift-type:0 1px 0 rgba(255,255,255,.95), 0 0 34px rgba(255,255,255,.9), 0 0 90px rgba(255,255,255,.75);

  /* ── TYPE SCALE ───────────────────────────────────────────────────── */
  --wordmark:clamp(52px,12vw,240px);
  --byline:clamp(22px,2.2vw,46px);
  --h1:clamp(46px,6vw,110px);
  --h2:clamp(32px,3.4vw,62px);
  --h3:clamp(26px,2.3vw,40px);
  --copy:clamp(20px,1.6vw,28px);
  --cap:clamp(17px,1.15vw,21px);

  /* ══ THE VERTICAL RHYTHM ══════════════════════════════════════════
     TWO values govern every gap on this page.
     --rhythm  between major blocks. Identical everywhere — which is what
               puts a section label EXACTLY halfway between its neighbours,
               by construction rather than by nudging.
     --tight   inside one deliberate pairing only (a character + her card).
     No section, label or card carries its own top or bottom margin.     */
  --rhythm:clamp(120px,11vw,240px);
  --masthead-gap:clamp(40px,4.2vw,84px);  /* byline → nav only */
  --tight:clamp(34px,3.4vw,64px);
  --guide-gap:clamp(84px,8.4vw,176px);   /* character → her card */

  /* ── ONE PILL · ONE BUTTON · ONE CARD ─────────────────────────────── */
  --pill-h:clamp(54px,3.9vw,68px);
  --pill-px:clamp(24px,2.1vw,38px);
  --btn-h:clamp(62px,4.4vw,76px);
  --btn-px:clamp(26px,2.3vw,42px);
  --radius:28px;
  --pad:clamp(64px,6vw,120px);      /* card breathing room — vertical */
  --pad-x:clamp(30px,2.8vw,52px);   /* horizontal, so a button still fits */
  --card-gap:clamp(40px,3.8vw,80px); /* between cards on a shelf */      /* card breathing room */
  --measure:60ch;
  --shell:1400px;
  --gutter:clamp(18px,3vw,54px);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}

body{
  margin:0;
  font-family:'DM Sans',system-ui,sans-serif;
  font-size:var(--copy);
  line-height:1.85;
  color:var(--ink);
  background:var(--white);
  overflow-x:hidden;
}

/* ══ THE SKY ══════════════════════════════════════════════════════════ */
.sky{
  position:fixed; inset:-40px; z-index:0;
  /* ⚠ 2026-08-09 — the sky is a 1600x2144 portrait used full-bleed on a
     landscape screen, so only a slice of it shows. Anchored at `top` that slice
     was exactly the big pink heart and the yellow star, and the wordmark landed
     on both: 18% of the title's area sat on saturated artwork. Scanning the
     actual pixels for the crop that puts the least art behind the title across
     1280 / 1440 / 1990 gives 35% — the heart and star move to frame the
     wordmark instead of colliding with it, and art under the title drops to
     6.4%. This moves the CROP, not one pixel of assets/sky.jpg.            */
  /* Rooms use a copy with only the large sliced yellow star painted out.
     sky.jpg is frozen. Storefront overrides this with sky-storefront.jpg. */
  background:url("assets/sky-rooms.jpg") center 35%/cover no-repeat;
  /* no blur, no wash — the sky is sharp and full colour. Display type is
     black with a white glow, so it does not need the background held back. */
  /* a fixed 40px bleed covers the blur edge without a percentage bleed or a
     scale() — both of which leaked into scroll width on a phone */
}
.sky::after{content:none}

.page{position:relative; z-index:2}

/* ══ THE GEMS — they rain across the whole hall ═══════════════════════
   Decorative only. The field sits on layer 0; ALL content sits on layer 1,
   so no stone can ever cover a word, a card or a button. Positions come
   from generated nth-child rules — collision-checked, never hand-placed.  */
.gemfield{position:absolute; inset:0; z-index:0; pointer-events:none; overflow:hidden}
.gem{
  position:absolute; height:auto; display:block;
  filter:drop-shadow(0 12px 26px rgba(0,0,0,.26));
  animation-name:float; animation-timing-function:ease-in-out;
  animation-iteration-count:infinite;
}
@keyframes float{
  0%,100%{transform:translateY(0) rotate(0deg)}
  50%{transform:translateY(-18px) rotate(3deg)}
}
.top,.hall{position:relative; z-index:1}

/* ══ MASTHEAD ═════════════════════════════════════════════════════════ */
.top{
  display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:var(--masthead-gap);
  padding:var(--rhythm) var(--gutter) 0;
  max-width:none; margin:0 auto;      /* full-bleed: the wordmark is not
                                         confined to the content shell */
}
.mark{
  font-family:'Playfair Display',Georgia,serif;
  font-weight:900; font-size:var(--wordmark);
  line-height:.96; letter-spacing:.005em;
  color:var(--pink);
  /* ⚠ 2026-08-09 — the sky has a big pink heart and a pink star behind the
     wordmark, and pink type on pink art loses its edge. An earlier session
     "fixed" this by repainting the heart white INSIDE assets/sky.jpg, which
     flattened her 3D shading and was reverted. The artwork is not the problem
     to solve. This does it in type instead: a real white outline on every
     letter plus a tight white halo, so the wordmark carries its own edge over
     anything the sky puts behind it. NEVER touch assets/sky.jpg for this.   */
  -webkit-text-stroke:clamp(2px,.4vw,8px) var(--white);
  paint-order:stroke fill;
  text-shadow:0 0 10px #fff, 0 0 22px rgba(255,255,255,.98),
              0 0 46px rgba(255,255,255,.75), 0 12px 34px rgba(255,62,175,.30);
  margin:0; text-align:center; text-wrap:balance;
  max-width:100%; min-width:0;   /* never wider than the viewport */
  position:relative;
}
/* ⛔ NO VEIL, NO FIELD, NO SCRIM BEHIND THE WORDMARK — REMOVED 2026-08-09.
   Two versions of a white radial field were tried here. The first washed out
   her fat 3D stars and hearts. The second was tightened to hug the type, and
   its EDGE showed as a visible horizontal line straight across the hero.
   Allie saw it immediately. A soft gradient cannot hide its own boundary over
   a photographic sky. The wordmark's readability comes from the white
   text-stroke on the glyphs, which follows the letterforms and has no edge.
   DO NOT reintroduce a plate, panel, scrim, veil or radial field here.     */
/* ⚠ 2026-08-09 — THE HALO HUGS THE LETTERS. NO PANEL, NO FIELD, NO SCRIM.
   Allie: "I LOVE THE HUGE FAT STARS AND HEARTS AND NOW YOU CANT SEE THEM."
   A radial field behind the wordmark was tried and REMOVED: a gradient covers
   an ellipse, so it cannot tell her 3D stars and hearts from the type, and it
   washed the artwork pale for hundreds of pixels in every direction. The
   readable edge has to follow the GLYPHS, not a box — that is the white
   text-stroke above plus the tight halo below, and it costs the sky nothing.
   Do not reintroduce a plate, panel, scrim or radial field behind this
   wordmark. The art stays at full saturation.
   Recorded so it is not re-attempted: no crop of this sky clears the title.
   1,300+ combinations of background-size and background-position were scored
   against the real pixels at 1280 / 1440 / 1700 / 1990. Best achievable is
   3.4%, and only at a 380% zoom that destroys the composition — it is a
   1600x2144 portrait with art edge to edge and the wordmark spans ~97% of the
   viewport. A fully clear hero needs NEW artwork with an open centre band,
   never a reposition and never a repaint of assets/sky.jpg.                */
.mark small{
  display:block;
  font-family:'DM Sans',sans-serif; font-weight:700;
  font-size:var(--byline); letter-spacing:.2em;
  color:var(--ink); -webkit-text-stroke:0;
  text-shadow:0 1px 0 rgba(255,255,255,.9), 0 0 18px rgba(255,255,255,.85);
  margin-top:var(--tight);
}
.nav{
  display:grid; grid-auto-flow:column; grid-auto-columns:minmax(0,1fr);
  gap:clamp(14px,1.2vw,22px); width:min(100%,1080px);
}
.nav a{
  display:inline-flex; align-items:center; justify-content:center;
  font-weight:700; font-size:clamp(19px,1.45vw,26px); letter-spacing:.13em;
  text-transform:uppercase; text-decoration:none; text-wrap:balance;
  color:var(--ink); background:var(--glass);
  border:3px solid var(--edge); border-radius:999px;
  min-height:var(--btn-h); padding:.7em 1.2em;
  backdrop-filter:blur(8px); box-shadow:var(--lift);
}
.nav a:hover,.nav a:focus-visible{background:var(--pink); color:var(--white); border-color:var(--pink)}

/* ══ THE HALL — the rhythm lives here and NOWHERE else ════════════════
   Every direct child is a block. One gap between all of them. A label
   dropped between two cards is therefore exactly halfway by construction. */
.hall{
  display:flex; flex-direction:column; align-items:center;
  gap:var(--rhythm);
  max-width:var(--shell); margin:0 auto;
  padding:var(--rhythm) var(--gutter);
}
.hall > *{width:100%; margin:0}

/* the ONE place a smaller gap is allowed: a character and her own card */
.pair{display:flex; flex-direction:column; align-items:center; gap:var(--guide-gap)}

/* ══ TYPE ═════════════════════════════════════════════════════════════ */
h1,h2,h3{font-family:'Playfair Display',Georgia,serif; text-transform:uppercase; margin:0}
h1,h2.section{
  font-weight:900; line-height:1.06; letter-spacing:.005em;
  color:var(--ink); text-shadow:var(--lift-type);
  text-align:center; text-wrap:balance; overflow-wrap:break-word;
}
h1{font-size:var(--h1)}
h2.section{font-size:var(--h2)}
.lede{
  font-size:var(--copy); line-height:1.55; color:var(--ink); text-align:center;
  text-transform:uppercase; letter-spacing:.04em; font-weight:700;
  /* the lede is a short SIGNATURE STATEMENT, not a paragraph — it stays
     ALL CAPS per the typography lock. Sentence case beside the sparkles
     read wrong. */
  background:var(--glass); border:2px solid var(--edge); border-radius:var(--radius);
  padding:var(--tight) var(--pad); margin:0 auto;
  width:max-content; max-width:min(100%,72ch);   /* hugs its own line, so a short
                                 lede is a snug plate and a long one still fits */ backdrop-filter:blur(10px); box-shadow:var(--lift);
}
/* the sparkle is the ONE yellow on the page — Allie 2026-08-08, a named
   exception to the three-colour lock. Emoji, so it carries its own colour. */
.spark{
  font-family:"Apple Color Emoji","Segoe UI Emoji",sans-serif;
  font-size:.86em; line-height:1; vertical-align:.04em;
  -webkit-text-stroke:0; text-shadow:none;
}

.anthem{
  font-family:'Playfair Display',Georgia,serif; font-weight:900;
  font-size:var(--h2); line-height:1.16; text-transform:uppercase;
  color:var(--ink); text-shadow:var(--lift-type); text-align:center;
  max-width:38ch; margin:0 auto; text-wrap:balance;
  hyphens:none; word-break:keep-all;
}

/* the sparkle is the ONE yellow on the page — Allie 2026-08-08, a named
   exception to the three-colour lock. Emoji, so it carries its own colour. */
.spark{
  font-family:"Apple Color Emoji","Segoe UI Emoji",sans-serif;
  font-size:.86em; line-height:1; vertical-align:.04em;
  -webkit-text-stroke:0; text-shadow:none;
}

/* ══ ONE PILL COMPONENT — every tag on the page is this and only this ══ */
.pill{
  display:inline-flex; align-items:center; justify-content:center;
  font-family:'DM Sans',sans-serif; font-weight:700;
  font-size:var(--cap); letter-spacing:.2em; line-height:1.25;
  text-transform:uppercase; text-align:center;
  color:var(--white); background:var(--pink);
  border:3px solid var(--pink); border-radius:999px;
  min-height:var(--pill-h); padding:0 var(--pill-px);
  width:max-content; max-width:100%;
  margin-left:auto; margin-right:auto;
  box-shadow:var(--lift);
}
/* a price is the same pill with a fixed width, so all prices match exactly */
.price{width:clamp(112px,8vw,150px)}

/* ══ ONE BUTTON COMPONENT ═════════════════════════════════════════════ */
.act{
  display:inline-flex; align-items:center; justify-content:center; gap:.6em;
  font-family:'DM Sans',sans-serif; font-weight:700;
  font-size:var(--cap); letter-spacing:.13em; line-height:1.25;
  text-transform:uppercase; text-decoration:none; text-align:center;
  color:var(--white); background:var(--pink);
  border:3px solid var(--pink); border-radius:999px;
  min-height:var(--btn-h); padding:.5em clamp(18px,1.5vw,28px);
  width:100%;
  /* ⚠ THIS WAS `white-space:nowrap`, AND IT WAS CUTTING THE LABELS OFF.
     nowrap was chosen so a button "never breaks into a blob". But a
     button is width:100% of a fixed-width card — it cannot grow sideways
     — so when the label is wider than the card, nowrap does not widen
     anything. It just overflows a rounded, clipping box, silently:

       390px  "SEND ME THE FREE RI AUDIT NOW"  ->  "END ME THE FREE RI AUDIT N"
       390px  "SEND ME A LITTLE ROYAL HOPE"    ->  clipped both ends
       1920px "SEE WHAT IS INSIDE"             ->  the heart icon cut in half

     Measured 2026-08-14 at 8 widths on all 8 pages: 40 clipped elements,
     the worst of them the FREE lead-magnet CTA on a phone. Nothing threw,
     nothing overflowed the page, and the collision gate passed all of it.

     A long label now buys a TALLER button, never smaller type and never a
     cut-off word — the same rule the nameplates already follow. Words stay
     whole (overflow-wrap stays at its default), so this can never produce
     a mid-word break; buttons in an .acts row still match height, because
     that grid stretches its items. */
  white-space:normal;
  box-shadow:var(--lift);
  transition:transform .16s ease, background .16s ease, color .16s ease;
}
.act:hover,.act:focus-visible{transform:translateY(-3px)}
.act.ghost{background:var(--white); color:var(--ink); border-color:var(--pink)}
.act.ghost:hover,.act.ghost:focus-visible{background:var(--pink); color:var(--white)}
.act i{font-style:normal; font-size:.9em}

/* a row of buttons: equal columns, so two CTAs can never differ in width */
.acts{
  display:grid; gap:clamp(14px,1.2vw,22px);
  grid-auto-flow:column; grid-auto-columns:minmax(0,1fr);
  width:min(100%,var(--measure)); margin:0 auto;
}
.acts.one{grid-auto-flow:row; width:min(100%,520px)}

/* ══ ONE CARD ═════════════════════════════════════════════════════════ */
.card{
  background:var(--card); color:var(--ink); text-align:center;
  border:2px solid var(--edge); border-radius:var(--radius);
  padding:var(--pad) var(--pad-x); box-shadow:var(--lift);
  display:flex; flex-direction:column; align-items:center; gap:var(--tight);
}
.card h3{font-size:var(--h3); line-height:1.14; color:var(--ink); text-wrap:balance}
.card p{margin:0 auto; max-width:var(--measure)}
.gift .card{border-color:var(--edge-hi); border-width:3px}
.bump .card{border-style:dashed; border-width:3px; border-color:var(--edge-hi)}

/* a room with two characters stands them side by side, both on the same
   baseline and both at the shared character height */
.faces{display:flex; align-items:flex-end; justify-content:center;
       gap:var(--card-gap); width:100%}

/* ══ THE CHARACTER — ABOVE her card, on the same centre axis ══════════ */
/* Characters are sized by HEIGHT, never width. Every master PNG is 900px tall,
   but their widths differ wildly — a star is wide and short, a pineapple is tall
   and narrow. Sizing by width made Bubbles render 248px tall and Miss Data-Riffic
   941px. Height keeps all seven the same visual size. Pinned so Penelope is
   unchanged from the frozen master. */
.face{
  display:block; height:clamp(300px,42.1vw,760px); width:auto;
  max-width:min(100%,60vw); margin:0 auto;
  filter:drop-shadow(0 26px 40px rgba(0,0,0,.34));
}

/* ══ THE SHELF — identical reserved areas, no per-card margins ════════
   Four subgrid rows: TITLE · PRICE · DESCRIPTION · BUTTON. Every card
   reserves the same height for each row, so a title that wraps to three
   lines cannot push its price pill off the shared baseline.             */
.shelf{
  display:grid; gap:var(--card-gap); justify-content:center;
  /* DOUBLE columns, each card spanning two. That is what lets a leftover row of
     one or two cards CENTRE itself — three equal columns cannot do it. Column
     count is explicit per breakpoint so the centring rules stay predictable. */
  grid-template-columns:repeat(6,minmax(0,1fr));
  grid-template-rows:min-content min-content 1fr min-content;
}
/* a room with fewer products than columns centres them instead of leaving a
   lone card stranded in column one. The count comes from the data, not by hand. */
/* the home shelf carries a guide portrait above each room name, so it has a
   fifth subgrid row. Every room card shows its character — the entrance to the
   kingdom must look like the kingdom. */
.shelf.guides{grid-template-rows:min-content min-content min-content 1fr min-content}
.shelf.guides > .item{grid-row:span 5}
/* ⚠ RESTORED 2026-08-09 to the APPROVED storefront value.
   Approved (`_BACKUPS/2026-08-09/CLOUDFLARE — index.html (OLD SINGLE-FILE,
   PRE-REBUILD)`) sized the guides from `--face-h: clamp(300px,29vw,470px)` —
   measured 418px tall at 1440 across all seven. Today's rebuild replaced it
   with clamp(190px,13vw,260px) AND moved the room cards from two across at
   660px to three across at 401px, which caps Bubbles (the widest master,
   aspect 1.29) at 249px inside a 321px content box. Both had to come back or
   the height alone would clamp her and break the row. Restoration, not design. */
.shelf.guides .face{height:clamp(300px,29vw,470px); max-width:100%; align-self:end}
/* two guides keep the door means two pills, side by side and identical */
.kickers{display:flex; flex-wrap:wrap; justify-content:center; gap:clamp(12px,1.2vw,22px)}
.kickers .pill{width:clamp(240px,20vw,340px)}

/* a card may name more than one guide — the pills stack, still one component */
/* EVERY name pill is the SAME BOX — one width, one height, no exceptions.
   The type shrinks to fit the longest name rather than the box growing. */

/* a full grid whose LAST row holds a single card centres it rather than
   stranding it in column one */
/* a leftover last row centres itself: one card → columns 3–4,
   two cards → columns 2–3 and 4–5 */
.item{
  display:grid; grid-row:span 4; grid-column:span 2;
  grid-template-rows:subgrid;
  grid-template-columns:minmax(0,1fr);   /* the card's inner column is the FULL
                                            content width — never sized by whichever
                                            title happens to be longest */
  gap:var(--tight); height:100%;
  transition:transform .18s ease, box-shadow .18s ease;
}
.item:hover{transform:translateY(-6px); box-shadow:var(--lift-hi)}
.item > *{justify-self:center; align-self:start}
.item .act{align-self:end}

/* ══ FOOT ═════════════════════════════════════════════════════════════ */
.foot{
  position:relative; z-index:2;
  background:none;                     /* no band, no veil — the sky runs to the bottom */
  padding:var(--rhythm) var(--gutter); text-align:center;
}
.foot p{
  margin:0 auto var(--tight); max-width:var(--measure);
  font-size:var(--cap); line-height:1.6; letter-spacing:.06em;
  color:var(--ink); text-shadow:var(--lift-type);
  text-transform:uppercase;
}
.foot p:last-child{margin-bottom:0}

a:focus-visible{outline:4px solid var(--white); outline-offset:4px}

/* ══ TABLET ═══════════════════════════════════════════════════════════ */
@media (max-width:1080px){
  /* the 6-column base makes each card span 2 — reset that once the grid narrows,
     or a card swallows a whole row */
  .face{height:clamp(280px,34vw,520px); max-width:76%}
}

/* ══ PHONE ════════════════════════════════════════════════════════════ */
@media (max-width:760px){
  :root{--radius:22px; --wordmark:11.2vw}  /* the 52px floor is wider than a 390px screen */
  /* ⚠ --h1 HAS THE SAME FLOOR BUG THE WORDMARK HAD, AND IT BIT.
     --h1 is clamp(46px,6vw,110px). At 390 the 6vw term is 23px, so the
     46px FLOOR wins — and "EGGXCELLENCE'S" at 46px is 420px wide inside
     a 354px box. It does not overflow and it does not error: CSS just
     chops it, and PRINCESS EGGXCELLENCE'S CASTLE renders as
     "PRINCESS / EGGXCELLENC / E'S CASTLE" on a phone. Her flagship
     character's name, split mid-word, silently, on every phone.
     Measured 2026-08-14 at 430/390/360 — the only mid-word break in the
     whole storefront. 9.6vw is set from the longest word in the catalog:
     it keeps EGGXCELLENCE'S whole down to 320px and stays capped at the
     full 46px on every screen 479px and wider, so no other room title
     gets smaller than it is today. Verified on all 7 rooms.
     If a longer room title is ever added, re-run tools/find-broken-words.py. */
  :root{--h1:clamp(30px,9.6vw,46px)}
  .nav{grid-auto-flow:row; grid-auto-columns:auto; width:100%}
  .pill{white-space:normal; padding:.7em var(--pill-px)}
  .face{height:clamp(230px,52vw,380px); max-width:86%}
  .mark small{white-space:normal; font-size:clamp(15px,4vw,22px); letter-spacing:.12em}
  /* on a phone a long name may wrap rather than run off the pill */
    .item{grid-row:auto; grid-template-rows:none; display:flex; flex-direction:column}
  .item .act{margin-top:auto}
.anthem{font-size:clamp(24px,6.6vw,32px)}
  .acts{grid-auto-flow:row}
}

@media (prefers-reduced-motion:reduce){
  .act,.item,.nav a{transition:none}
  .gem{animation:none}
}

/* ══ GEM FIELD · hope-diamond-hall — GENERATED BY tools/regen-gems.py — DO NOT HAND-EDIT ══
   Six breakpoint bands. Each set is generated in normalised page space
   against the UNION of content boxes measured at both ends of its band,
   so a stone stays clear of content across the whole range. Re-run the
   tool after ANY layout change or stones will land on top of content. */
@media (max-width:1099px){ .gemfield{display:none} }
/* ══ END GEM FIELD · hope-diamond-hall ══ */

/* ══ GEM FIELD · castle — GENERATED BY tools/regen-gems.py — DO NOT HAND-EDIT ══
   Six breakpoint bands. Each set is generated in normalised page space
   against the UNION of content boxes measured at both ends of its band,
   so a stone stays clear of content across the whole range. Re-run the
   tool after ANY layout change or stones will land on top of content. */
@media (max-width:1099px){ .gemfield{display:none} }
/* ══ END GEM FIELD · castle ══ */

/* ══ GEM FIELD · library — GENERATED BY tools/regen-gems.py — DO NOT HAND-EDIT ══
   Six breakpoint bands. Each set is generated in normalised page space
   against the UNION of content boxes measured at both ends of its band,
   so a stone stays clear of content across the whole range. Re-run the
   tool after ANY layout change or stones will land on top of content. */
@media (max-width:1099px){ .gemfield{display:none} }
/* ══ END GEM FIELD · library ══ */

/* ══ GEM FIELD · ballroom — GENERATED BY tools/regen-gems.py — DO NOT HAND-EDIT ══
   Six breakpoint bands. Each set is generated in normalised page space
   against the UNION of content boxes measured at both ends of its band,
   so a stone stays clear of content across the whole range. Re-run the
   tool after ANY layout change or stones will land on top of content. */
@media (max-width:1099px){ .gemfield{display:none} }
/* ══ END GEM FIELD · ballroom ══ */

/* ══ GEM FIELD · sanctuary — GENERATED BY tools/regen-gems.py — DO NOT HAND-EDIT ══
   Six breakpoint bands. Each set is generated in normalised page space
   against the UNION of content boxes measured at both ends of its band,
   so a stone stays clear of content across the whole range. Re-run the
   tool after ANY layout change or stones will land on top of content. */
@media (max-width:1099px){ .gemfield{display:none} }
/* ══ END GEM FIELD · sanctuary ══ */

/* ══ GEM FIELD · manor — GENERATED BY tools/regen-gems.py — DO NOT HAND-EDIT ══
   Six breakpoint bands. Each set is generated in normalised page space
   against the UNION of content boxes measured at both ends of its band,
   so a stone stays clear of content across the whole range. Re-run the
   tool after ANY layout change or stones will land on top of content. */
@media (max-width:1099px){ .gemfield{display:none} }
/* ══ END GEM FIELD · manor ══ */

/* ══ GEM FIELD · meadow — GENERATED BY tools/regen-gems.py — DO NOT HAND-EDIT ══
   Six breakpoint bands. Each set is generated in normalised page space
   against the UNION of content boxes measured at both ends of its band,
   so a stone stays clear of content across the whole range. Re-run the
   tool after ANY layout change or stones will land on top of content. */
@media (max-width:1099px){ .gemfield{display:none} }
/* ══ END GEM FIELD · meadow ══ */

/* ══ GEM FIELD · index — GENERATED BY tools/regen-gems.py — DO NOT HAND-EDIT ══
   Six breakpoint bands. Each set is generated in normalised page space
   against the UNION of content boxes measured at both ends of its band,
   so a stone stays clear of content across the whole range. Re-run the
   tool after ANY layout change or stones will land on top of content. */
@media (max-width:1099px){ .gemfield{display:none} }
/* ══ END GEM FIELD · index ══ */

/* ══ NAME PILLS — ONE RULE, LAST WORD ═════════════════════════════════
   Every guide pill is the full width of its card, so pills in a row are
   always identical. It wraps rather than overflowing, and keeps one
   minimum height so a one-line and a two-line pill still read as the
   same component. Declared last so nothing above can contradict it.   */
.names{display:flex; flex-direction:column; align-items:stretch;
       gap:12px; width:100%}
.names .pill{
  width:100%; max-width:100%;
  min-height:var(--pill-h); height:auto;
  padding:.55em 14px;
  letter-spacing:.06em; line-height:1.25;
  font-size:clamp(13px,.95vw,16.5px);
  white-space:normal; text-align:center;
}

/* ══ LEFTOVER ROWS CENTRE THEMSELVES ══════════════════════════════════
   3 across (6 cols): a lone last card sits in cols 3-4; a last pair starts
   at col 2 so it occupies 2-3 and 4-5. 2 across (4 cols): a lone last card
   sits in cols 2-3. 1 across: nothing to centre.

   ⚠ CORRECTED 2026-08-09 — the ladder must be MUTUALLY EXCLUSIVE.
   These centring rules were previously declared unscoped, outside any media
   query. A media query adds NO specificity, so `.shelf > .item{grid-column:
   span 2}` (0,2,0) inside the narrow blocks could never override
   `.shelf > .item:last-child:nth-child(3n+1)` (0,4,0) declared above it.
   Below 828px the one-column grid therefore kept `grid-column:3/span 2`,
   Chrome created implicit columns to satisfy it, and the LAST card of every
   shelf rendered at half width, shoved right off the centre axis, pushing
   the page into horizontal overflow on a phone. It hit all eight pages —
   index card 7, hall 3, castle 9, library 9, sanctuary 4+5, manor 1,
   ballroom 1, meadow 1 — and the gold master failed its own gate at 390px.
   The fix is range-bounding each rung so no two rungs are ever live at the
   same width. Nothing is weakened and no !important is used: at 828px and
   above the computed layout is byte-identical to before.               */
@media (min-width:1228px){
  .shelf > .item:last-child:nth-child(3n+1){grid-column:3/span 2}
  .shelf > .item:nth-last-child(2):nth-child(3n+1){grid-column:2/span 2}
  /* THE HOME SHELF IS TWO ACROSS — restored to the approved storefront, which
     ran the seven room cards 2/2/2/1 at 660px. Only .shelf.guides widens; a
     room's product shelf stays three across. These selectors carry one more
     class than the generic rules above, so they win without !important and the
     breakpoint ladder stays mutually exclusive.                            */
  .shelf.guides{grid-template-columns:repeat(4,minmax(0,1fr))}
  .shelf.guides > .item{grid-column:span 2}
  .shelf.guides > .item:last-child:nth-child(3n+1){grid-column:auto/span 2}
  .shelf.guides > .item:nth-last-child(2):nth-child(3n+1){grid-column:auto/span 2}
  .shelf.guides > .item:last-child:nth-child(2n+1){grid-column:2/span 2}
}
@media (min-width:828px) and (max-width:1227px){
  .shelf{grid-template-columns:repeat(4,minmax(0,1fr))}
  .shelf > .item{grid-column:span 2}
  .shelf > .item:last-child:nth-child(2n+1){grid-column:2/span 2}
}
@media (max-width:827px){
  .shelf{grid-template-columns:repeat(2,minmax(0,1fr))}
  .shelf > .item{grid-column:span 2}
}
