/* ==========================================================================
   Crux V6 - Final production polish
   ==========================================================================
   Loaded last on every V6 route, after light.css. Everything here is one of
   the changes signed off for the production pass:

     1. Sticky offsets, shared
     2. The Login link, desktop and mobile
     3. Section seams: no more hard colour edges
     4. The hero: air around the headline, a wider instrument
     5. The QuickBooks lockup
     6. The Control Tower reads as operable
     7. The sequence ends on Book a Demo
     8. The seven-step workflow stays usable while you scroll it
     9. The estimator opens in place
    10. Small consistency corrections found in the route review

   Removals live at their source (the square-pattern backgrounds are gone from
   base.css, elevation.css, home.css, light.css and tower.css) rather than
   being overridden here, so nothing is being painted and then hidden.

   Specificity: the V5 selectors this layer sits on top of are mostly
   `.home <thing>` (0,1,1), so home-family rules here carry `.home` too and
   marketing-family rules carry `body:not(.home)`. Source order alone does not
   win against them.
   ========================================================================== */

:root {
  /* The masthead is sticky, and 73px tall until the brand mark steps up at
     60rem, where it becomes 89. Note that this is NOT where the desktop
     navigation appears (64rem): between 960 and 1024 the bar is already tall
     while the menu button is still showing, and keying this to 64rem slid the
     workflow's sticky header 16px under the bar through that whole band.
     Anything else that sticks has to clear the masthead, and there is exactly
     one number to keep in step with it. */
  --stick-top: 73px;
  /* How far a surface change is spread over. Inside the section padding at
     every breakpoint, so a seam never runs under content. */
  --seam-h: clamp(3rem, 6vw, 5.5rem);
  --seam-from: transparent;
  /* The gap a heading keeps below the foot of the band. This is not a new
     number: it is the clearance a full-rhythm section already has, --bay minus
     --seam-h, which runs 20px at 1024 and 29px at 1440. Surfaces that do not
     run on --bay are held to the same figure below, so every heading on the
     site starts the same distance into solid colour. */
  --seam-clear: clamp(1.25rem, 2vw, 1.75rem);
}
@media (min-width: 60.0625rem) {
  :root { --stick-top: 89px; }
}

/* ==========================================================================
   2. THE LOGIN LINK
   ==========================================================================
   A quiet text-and-icon link, not a second button: the primary action in this
   bar is Book a Demo and there is no version of this where Login competes
   with it. It sits immediately before the CTA, which is where a returning
   customer looks, and it appears and disappears with the desktop navigation.
   The icon is decorative; the word carries the meaning, so the icon is
   aria-hidden and the label is real text.
   ========================================================================== */

.navlogin {
  display: none;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--font-body);
  font-size: var(--type-small);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text-muted, rgba(255,255,255,0.82));
  white-space: nowrap;
  /* 44px of target on a 20px-tall link, without changing the bar's height. */
  min-height: 44px;
  padding-inline: 0.35rem;
  border-radius: var(--radius-sm, 6px);
}
.navlogin:hover { color: var(--text-strong, #fff); }
.navlogin:focus-visible {
  outline: 2px solid var(--focus, #FFDA00);
  outline-offset: 2px;
}
.navlogin__icon { flex: 0 0 auto; opacity: 0.9; }
.navlogin:hover .navlogin__icon { opacity: 1; }

@media (min-width: 64rem) {
  .navlogin { display: inline-flex; }
  /* The bar is brand / nav / login / CTA at 1024, which is where it is
     tightest. Pulling the gap in keeps all four on one line there. */
  .masthead__inner { gap: var(--space-3); }
}
@media (min-width: 75rem) {
  .masthead__inner { gap: var(--space-4); }
}

/* Mobile: the last row of the menu, above the CTA block, with the same
   44px row height as every other row in the panel. */
.mobilenav__link--login {
  margin-top: var(--space-2);
  border: var(--border-1, 1px) solid var(--border, rgba(255,255,255,0.12));
  color: var(--text-strong, #fff);
}
.mobilenav__loginwrap { display: inline-flex; align-items: center; gap: 0.55rem; }
.mobilenav__link--login .navlogin__icon { opacity: 0.85; }

/* ==========================================================================
   3. SECTION SEAMS
   ==========================================================================
   Every surface change on this site used to be a hard edge: paper stopped and
   navy started on the same pixel row. The light-to-dark treatment is the
   point and stays; what goes is the cut.

   A section that follows a different tone paints that tone across its own top
   and fades it out over --seam-h. The band sits inside the section's padding,
   so no content ever lands on it, and because it is drawn by the arriving
   section there is no way for the two halves of a seam to disagree.

   `--seam-from` is set per adjacency below and defaults to transparent, so a
   pair that is already the same tone draws nothing.
   ========================================================================== */

.home .stage,
.home .close-tower,
.home .sec,
body:not(.home) main > section { position: relative; }

.home .stage::before,
.home .close-tower::before,
.home .sec::before,
body:not(.home) main > section::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--seam-h);
  background: linear-gradient(180deg, var(--seam-from) 0%, transparent 100%);
  pointer-events: none;
  z-index: 0;
}
.home .stage > .container,
.home .close-tower > .container,
.home .sec > .container,
body:not(.home) main > section > .container { position: relative; z-index: 1; }

/* ---- The stage family: home, proof, resources ---------------------------- */
.home .tower + .stage--yard          { --seam-from: #f4f2ec; }
.home .stage--yard  + .stage--white  { --seam-from: #e9e5d9; }
.home .stage--yard  + .stage--paper  { --seam-from: #e9e5d9; }
.home .stage--yard  + .stage--tower-2 { --seam-from: #e9e5d9; }
.home .stage--white + .stage--paper  { --seam-from: #ffffff; }
.home .stage--white + .stage--tower-2 { --seam-from: #ffffff; }
.home .stage--white + .close-tower   { --seam-from: #ffffff; }
.home .stage--paper + .stage--white  { --seam-from: #f4f2ec; }
.home .stage--paper + .stage--tower-2 { --seam-from: #f4f2ec; }
.home .stage--paper + .close-tower   { --seam-from: #f4f2ec; }
.home .stage--tower-2 + .stage--white { --seam-from: #1a2660; }
.home .stage--tower-2 + .stage--paper { --seam-from: #1a2660; }
.home .stage--tower-2 + .stage--tower-1 { --seam-from: #1a2660; }
.home .stage--tower-1 + .stage--tower-0 { --seam-from: #131c4a; }
.home .stage--tower-0 + .close-tower  { --seam-from: #0b1233; }
.home .stage--tower-1 + .close-tower  { --seam-from: #131c4a; }
.home .stage--tower-2 + .close-tower  { --seam-from: #1a2660; }

/* ---- The capability family: platform pages, pricing ----------------------
   Default surface is the page paper; .sec--card is white; .sec--dark is navy.
   Order matters: every modifier below is also a .sec, so the generic pairs go
   first and the qualified ones override them. Same-tone pairs are last and
   paint nothing. */
.home .sec + .sec--card              { --seam-from: #f4f2ec; }
.home .sec + .sec--roles             { --seam-from: #f4f2ec; }
.home .sec + .sec--dark              { --seam-from: #f4f2ec; }
.home .sec--roles + .sec--card       { --seam-from: #f4f2ec; }

.home .sec--card + .sec              { --seam-from: #ffffff; }
.home .sec--card + .sec--roles       { --seam-from: #ffffff; }
.home .sec--card + .sec--dark        { --seam-from: #ffffff; }

.home .sec--dark + .sec              { --seam-from: #1a2660; }
.home .sec--dark + .sec--card        { --seam-from: #1a2660; }

.home .sec--card + .sec--card,
.home .sec--dark + .sec--dark        { --seam-from: transparent; }

/* ---- The marketing family: about, solutions, contact ---------------------
   .hero is paper here (light.css relights it). .section--navy and .cta-band
   keep the plate gradient, whose foot is --plate-3; the seam that leaves one
   has to start from that value, not from a guess, or the ramp begins with a
   visible step. */
body:not(.home) main > .hero + .section                 { --seam-from: #f4f2ec; }
body:not(.home) main > .section + .section--paper       { --seam-from: #f4f2ec; }
body:not(.home) main > .section--paper + .section       { --seam-from: #ffffff; }
body:not(.home) main > .section + .cta-band             { --seam-from: #f4f2ec; }
body:not(.home) main > .section--paper + .cta-band      { --seam-from: #ffffff; }
body:not(.home) main > .hero + .section--navy           { --seam-from: #f4f2ec; }
body:not(.home) main > .section--navy + .section        { --seam-from: #0d1740; }
body:not(.home) main > .section--navy + .section--paper { --seam-from: #0d1740; }

/* ---- Clearance: the band has to fit in the padding that hosts it ---------
   The note at the top of this section claims the band always sits inside the
   section padding. That held for a section on the full --bay rhythm and for
   nothing else, because two rules put a marketing section on a shorter top
   padding while the band stayed at its full --seam-h:

     mobile.css   below 48rem every non-home section drops to 2.4rem (38px)
                  against a 48px band
     tower.css    .section--tight runs on --bay-tight, 41px at 1024 and 58px
                  at 1440, against a 61px and 86px band

   So the heading of every seam-painting section on contact, about and
   solutions started on the gradient rather than below it, worst at 1440 where
   "Tell us about your operation" began 29px inside an 86px band and read as
   floating in the seam.

   The band is the design and does not move. What moves is the floor under the
   padding that has to contain it: the band plus the standard clearance,
   applied only where a band is actually painted, so a section that paints
   nothing keeps its own tighter rhythm. .cta-band is excluded because its
   inner block already carries its own offset and clears at every width. */
@media (max-width: 48rem) {
  body:not(.home) main > .hero + section:not(.cta-band),
  body:not(.home) main > section + section:not(.cta-band) {
    padding-top: max(2.4rem, calc(var(--seam-h) + var(--seam-clear)));
  }
}
body:not(.home) main > .hero + .section--tight,
body:not(.home) main > section + .section--tight {
  padding-top: max(var(--bay-tight), calc(var(--seam-h) + var(--seam-clear)));
}

/* ---- The seam into the footer ------------------------------------------
   The footer is outside <main>, so no sibling selector reaches it from the
   last section. It is keyed off what that section is instead.

   Four of these were real edges. On the homepage the close plate is #0b1233
   and the footer is #1a2660, so the page got lighter at the very bottom on a
   hard line. On contact the last section is white and the footer is navy: the
   single hardest edge left on the site. About and Solutions step from the
   plate's foot to the footer's own gradient.

   :has() is doing the page-shape test. Where it is not supported the custom
   property simply stays transparent and the footer renders exactly as it did
   before, which is the correct thing for a decorative seam to do.

   The footer runs on its own rhythm, 4rem and 2.2rem below 48rem, not on
   --bay, so the full-height band overhung it the same way it overhung the
   tight sections: the brand mark sat 13px into the band at 390 and 22px in at
   1440. A section can absorb that by taking more padding. The footer cannot,
   because its height is chrome shared by every route, so the band is scaled to
   the padding it actually has instead. Same gradient, same tone, same
   direction, sized to the surface that hosts it, and no route changes height. */
.footer {
  position: relative;
  --seam-h: clamp(1.25rem, 2.5vw, 2.5rem);
}
.footer::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--seam-h);
  background: linear-gradient(180deg, var(--seam-from) 0%, transparent 100%);
  pointer-events: none;
  z-index: 0;
}
.footer > .container { position: relative; z-index: 1; }

body:has(main > .close-tower:last-child) .footer { --seam-from: #0b1233; }
body:has(main > .cta-band:last-child) .footer { --seam-from: #0d1740; }
body:has(main > .section--paper:last-child) .footer { --seam-from: #ffffff; }
body:has(main > .sec:last-child:not(.sec--dark)) .footer { --seam-from: #f4f2ec; }
/* A hairline drawn across the middle of a graded seam is the artefact the
   seam exists to remove. Zero width rather than a transparent colour: the
   footer's own background paints under the border box, so a transparent 1px
   border still showed a 1px band of footer navy above the seam. Where the
   tones either side already match, the rule is the only separation there is
   and it stays. */
body:has(main > .close-tower:last-child) .footer,
body:has(main > .cta-band:last-child) .footer,
body:has(main > .section--paper:last-child) .footer,
body:has(main > .sec:last-child:not(.sec--dark)) .footer { border-top: 0; }

/* The same hairlines, one level up: three section rules that were drawn when
   every boundary was a cut. The graded seam has replaced each of them.
   .close keeps no yellow rule for the same reason the threshold mark went. */
body:not(.home) .cta-band { border-top: 0; border-bottom: 0; }
body:not(.home) .section--paper { border-block: 0; }
.home .close { border-top: 0; }

/* The threshold mark is gone. It was a 2px yellow slab across the full width,
   which is the single hardest edge on the site, and a decorative rule sitting
   inside a graded seam reads as an artefact rather than as a mark. Entering
   the tower is still announced, by the thing that was always announcing it:
   the stage header, "04 THE BOUNDARY", set in the signal colour. */
.stage--threshold { border-top: 0; }

/* ==========================================================================
   4. THE HERO
   ==========================================================================
   Three complaints, one cause: the copy column carried six blocks, the panel
   was pinned to a narrow track, and the two columns were close enough to read
   as one crowded band.
   ========================================================================== */

/* Air. The gap between the copy and the instrument was 2.5-4rem; at 1024 that
   put the panel's shadow across the lede. */
.home .tower__grid { gap: clamp(3rem, 5.5vw, 6.5rem); }

/* The instrument gets the wider half. The copy column is a 15ch headline and
   a 46ch lede, neither of which needed the room; the panel is a queue, three
   jobs, a decision row and a six-step sequence, all of which did. */
@media (min-width: 62rem) {
  .home .tower__grid { grid-template-columns: minmax(0, 0.88fr) minmax(0, 1.12fr); }
}
@media (min-width: 82rem) {
  /* Above 1312 the default 74rem container leaves the instrument narrower
     than the content inside it wants. The hero, and only the hero, uses the
     wide container, so the panel gains width from the page rather than from
     the copy: at 1440 it goes from 546px to 615 while the lede holds a 52
     character measure. Taking the width off the copy column instead pushed
     the lede to 39 characters a line, under the 40 floor this build holds
     every body measure to. */
  .home .tower > .container { max-width: 82rem; }
  .home .tower__grid { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); }
}

/* Room above and below the headline. The eyebrow was 1.4rem off the title and
   the title 1.3rem off the next block, which read as one dense stack. */
.home .tower__eyebrow { margin-bottom: 1.15rem; }
.home .tower__title { margin-bottom: 1.75rem; }
.home .tower__lede { margin-bottom: 2.25rem; }
.home .tower__actions { margin-bottom: 0; }

/* ==========================================================================
   5. THE QUICKBOOKS LOCKUP
   ==========================================================================
   Four things have to be unmistakable near the top of the page: Crux works
   with QuickBooks, Crux does not replace it, QuickBooks stays the accounting
   system of record, and that relationship is the market position.

   They used to be spread across a label, a lede and a note, with the word
   QuickBooks appearing three times in one viewport saying overlapping things.
   Now they are one block: the official mark, then one sentence. Each fact is
   stated once.
   ========================================================================== */

.home .heroqb {
  margin: 0 0 1.9rem;
  padding-left: 1rem;
  border-left: 3px solid var(--qb-green, #1F7014);
}
.home .heroqb__lock {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 0.35rem 0.7rem;
  margin: 0 0 0.55rem;
}
.home .heroqb__claim {
  margin: 0;
  font-size: 0.95rem; line-height: 1.6;
  color: var(--ink-body, #3b426a);
  max-width: 44ch;
}
.home .heroqb__logo { height: 30px; }

@media (max-width: 61.99rem) {
  .home .heroqb { order: 3; margin-top: 0.15rem; }
  .home .heroqb__logo { height: 26px; }
}

/* ==========================================================================
   6. THE CONTROL TOWER READS AS OPERABLE
   ==========================================================================
   The panel was a working instrument that looked like a screenshot. Someone
   who did not scroll past the fold never learned it could be touched.

   Three cues, in the order the eye meets them: a badge in the header strip
   saying the panel is interactive, the invitation moved from the middle of
   the panel to the top of it, and a ring on the one control that starts the
   sequence. None of them changes the interaction or the visual language;
   they say out loud what the panel was already offering.
   ========================================================================== */

/* The invitation now sits between the header strip and the job list, so it is
   the first thing read inside the panel rather than something found on the
   way down. Its rule moves to the bottom edge to match. */
.home .invite {
  padding: 0.8rem 1rem 0.85rem;
  border-top: 0;
  border-bottom: 1px solid rgba(255, 218, 0, 0.28);
  align-items: center;
}
.home .invite__badge {
  font-family: var(--font-mono); font-size: 0.6rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--navy, #1a2660); background: var(--signal, #FFDA00);
  border-radius: 999px; padding: 3px 9px;
  white-space: nowrap;
}

/* The panel's own label, at the foot of the instrument. Mono and quiet: it is
   the housing speaking, not a person, and it must never compete with the
   figures above it. */
.home .panel__note {
  margin: 0;
  /* .home p is capped at 60ch, which left this strip stopping two thirds of
     the way across the instrument and reading as a stray block. */
  max-width: none;
  padding: 0.7rem 1rem 0.75rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(0, 0, 0, 0.18);
  font-family: var(--font-mono);
  font-size: 0.66rem; line-height: 1.55; letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.62);
}

/* The ring on Approve. A static outline does the work; the breath is a
   courtesy for anyone who has not noticed it, it runs on the ring rather than
   on the button so nothing moves or resizes, and under reduced motion the
   global rule collapses it to its resting frame, where the ring is still
   plainly there. */
.home .decision { position: relative; }
.home .decision[data-enhanced] .decision__act[aria-pressed="false"]:not(:disabled) {
  box-shadow: 0 0 0 3px rgba(255, 218, 0, 0.28);
}
@media (prefers-reduced-motion: no-preference) {
  .home .decision[data-enhanced] .decision__act[aria-pressed="false"]:not(:disabled) {
    animation: cruxCue 2.4s ease-in-out infinite;
  }
  @keyframes cruxCue {
    0%, 100% { box-shadow: 0 0 0 3px rgba(255, 218, 0, 0.30); }
    50%      { box-shadow: 0 0 0 7px rgba(255, 218, 0, 0.05); }
  }
}
/* The cue has done its job the moment the item is decided. */
.home .decision__act[aria-pressed="true"],
.home .decision__act:disabled { animation: none; }

/* ==========================================================================
   7. THE SEQUENCE ENDS ON BOOK A DEMO
   ==========================================================================
   The last control used to be Start again, which offered the visitor the one
   thing they had just finished. It is now the demo, with restart demoted to
   the quiet control beside it. The primary is a link, so it needs the button
   box the sim's own controls have.
   ========================================================================== */

a.sim__btn {
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none;
}
.home .sim__acts { align-items: center; gap: 0.6rem 0.75rem; }

/* ==========================================================================
   8. THE SEVEN-STEP WORKFLOW
   ==========================================================================
   The component was 1115px tall inside a 772px viewport, with the step
   tracker above the frame and the transport controls below it. There was no
   scroll position from which you could see the step you were on, the thing it
   was showing, and the button that advanced it. Pressing Next also moved
   focus to the frame's heading, which scrolled the page out from under the
   button that had just been pressed.

   Two changes fix it and neither touches the seven steps:

   a) The activity trail moves out of a full-width row under the frame and
      into the left column beneath the queue, which is where it belongs
      anyway: queue and trail are both the narrow context either side of the
      wide stage. That takes the tallest step from 863px to about 720.

   b) Tracker, progress and transport are one block that sticks under the
      masthead for as long as the frame is on screen. Wherever you are in the
      component, the step you are on and the control that advances it are
      both in view.

   The focus jump is fixed in demo.js with preventScroll.
   ========================================================================== */

/* The frame's footprint is fixed, but its interior reflows on every step, so
   the browser kept picking a node inside it as the scroll anchor and nudging
   the page 10 to 20px to hold that node still. From the visitor's side that
   is the page twitching every time they press Next. Nothing here needs
   anchoring: the footprint is pinned and the operating header is sticky. */
.home .demo { overflow-anchor: none; }

/* The sticky operating header. */
.home .demo__ops {
  position: sticky;
  top: var(--stick-top);
  z-index: 6;
  /* Section 02 is the white stage; the header has to occlude what scrolls
     under it, and the fade keeps the bottom edge from reading as a bar. */
  background: linear-gradient(180deg, #fff 0%, #fff 82%, rgba(255,255,255,0.92) 100%);
  padding-block: 0.85rem 0.9rem;
  /* Bleed to the container edge so nothing shows through beside it. */
  margin-inline: calc(var(--container-pad) * -0.5);
  padding-inline: calc(var(--container-pad) * 0.5);
}
.home .demo[data-enhanced] .demo__controls { margin-top: 0.75rem; }
.home .demo[data-enhanced] .demo__progress { margin-top: 0.75rem; }
.home .demo__frame { margin-top: clamp(1rem, 2vw, 1.35rem); }
/* Every pixel the header spends is a pixel the frame does not get. */
.home .demo__ops .demo__step { padding: 0.5rem 0.75rem 0.55rem; }

/* The reserved footprint, laid out rather than left over.
   demo.js pins the frame to the tallest step so the page does not resize
   under the visitor, which on the shortest step leaves about 280px spare.
   That used to be filled with a blueprint grid; with the square patterns
   gone it read as a rendering fault. So the stage column stretches to the
   reserved height and its three parts take up position in it: the heading
   and copy at the top, the scene centred in what is left, the step's
   footnote closed off at the foot above a hairline. The spare room stops
   being a hole and becomes the margin of a worksheet, which is what it
   always was. */
@media (min-width: 60rem) {
  /* The invoice record keeps its full footprint from the first step and fills
     its rows in, instead of growing a row at a time. Two things follow. The
     reserved height stops swinging by 279px across the seven steps and swings
     by 124 instead, which is most of the void gone at source. And the record
     now reads the way the step actually describes it: a structured record
     gathering context, rather than a list that happens to get longer.
     visibility rather than display, so the rows that are not there yet are
     out of the accessibility tree exactly as they were. */
  .home .demo[data-enhanced] .demo__rrow { display: flex; visibility: hidden; }
  .home .demo[data-enhanced] .demo__rrow.is-shown { visibility: visible; }

  .home .demo__stage {
    align-self: stretch;
    display: flex; flex-direction: column;
  }
  /* Two auto margins on the one visible scene: the free space splits evenly
     above and below it, and the footnote lands on the bottom edge. */
  .home .demo__scene:not([hidden]) { margin-block: auto; }
  .home .demo__note {
    padding-top: 1rem;
    border-top: 1px solid var(--line-dark, rgba(255,255,255,0.14));
    /* The rule reads as a mistake if it stops at the prose measure while the
       column it closes is wider. */
    max-width: none;
  }
}

/* Without JS the tracker, progress and transport are all display:none, so the
   wrapper would leave a sticky empty band across the top of a static page. */
.home .demo:not([data-enhanced]) .demo__ops { display: none; }

/* The trail joins the queue in the left column. */
@media (min-width: 60rem) {
  .home .demo__frame {
    grid-template-columns: 15rem minmax(0, 1fr);
    grid-template-areas: "queue stage" "trail stage";
    grid-template-rows: auto minmax(0, 1fr);
  }
  .home .demo__trailwrap { grid-area: trail; align-self: start; }
}

/* Mobile keeps its own order (mobile.css puts the transport above the frame
   already); the wrapper just has to not fight it. Sticky stays on, because a
   390-wide viewport is where losing the controls hurt most, but the header is
   compacted so it does not eat the screen. */
@media (max-width: 48rem) {
  .home .demo { display: block; }
  .home .demo__ops { padding-block: 0.7rem 0.75rem; }
  .home .demo[data-enhanced] .demo__controls { margin-top: 0.7rem; }
}

/* Below the two-column layout the reserved footprint stops being a courtesy
   and becomes the problem. Stacked, the seven steps run from 852px to 1588,
   so pinning to the tallest leaves 736px of empty navy under the first step
   on a 390-wide screen: more than three quarters of the viewport, showing
   nothing. The reason for pinning was to stop the page resizing under the
   visitor, and the sticky operating header now does that job better, because
   the tracker and the transport hold their position whatever the frame does.
   So below 60rem the frame sizes to its content. !important beats the inline
   value demo.js writes, and its resize re-measure keeps working untouched. */
@media (max-width: 59.99rem) {
  .home .demo__frame { min-height: 0 !important; }
}

/* Two rows of transport, not three. The step counter had a row of its own at
   390, which cost 40px of a sticky header on the screen that can least afford
   it. */
@media (max-width: 40rem) {
  .home .demo__ops .demo__count { order: 0; width: auto; text-align: left; }
  .home .demo__ops .demo__controls .demo__back,
  .home .demo__ops .demo__controls .demo__next { flex: 1 1 auto; }
  .home .demo__ops .demo__controls .btn { padding-inline: 0.75rem; }
  /* Force the break after the transport so Play and Restart share the second
     row. Left to wrap on its own, Play landed beside Next and Restart got a
     third row to itself. */
  .home .demo__ops .demo__controls-sp { display: block; flex: 1 0 100%; height: 0; }
}

/* ==========================================================================
   9. THE ESTIMATOR OPENS IN PLACE
   ========================================================================== */

.home .roi-open {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 0.65rem 1rem;
  margin-bottom: 1.6rem;
}
.home .roi-open[hidden] { display: none !important; }
.home .roi-open__meta {
  font-family: var(--font-mono); font-size: 0.75rem; line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--ink-muted, #5c6485);
}
/* No caret glyph on the button. The two labels are "Open the estimator" and
   "Hide the estimator", which say the state and the action in words; a
   triangle set in DM Sans beside them rendered as an ambiguous speck and
   added nothing the label was not already carrying. */
.home .roi[hidden] { display: none !important; }
/* The panel has just appeared under the button that asked for it; a short
   settle says so without moving anything above it. */
@media (prefers-reduced-motion: no-preference) {
  .home .roi-open.is-open + .roi {
    animation: cruxOpen 220ms var(--ease-standard, ease) both;
  }
  @keyframes cruxOpen {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: none; }
  }
}

/* ==========================================================================
   10. CONSISTENCY CORRECTIONS FROM THE ROUTE REVIEW
   ========================================================================== */

/* The estimator section and the boundary section both open with a stage head
   and then a wide instrument. The estimator's head had no bottom margin of
   its own, so its lead ran into the opener. */
.home .stage--paper .stagehead { margin-bottom: var(--bay-tight); }

/* Clipping at 320, found in the route review. The answered register's chip
   carries white-space:nowrap so it reads as one token in the right-hand
   column it gets at 960 and up. Below that it has no column of its own, and
   "Answered at 05 Record decision" set in mono is 261px wide, which is wider
   than the whole 227px content track at 320. A 1fr track cannot shrink below
   its content's min-content width, so the row pushed 15px past the viewport
   and main's overflow:clip cut the text off.

   Two fixes, because either alone leaves the other hole open: the chip wraps
   where it has no column of its own, and every register track is minmax(0,
   1fr) so no future child can force the grid wider than its row again. */
@media (max-width: 59.99rem) {
  .home .register--answered .register__fix { white-space: normal; }
}
.home .register__row { grid-template-columns: 2.5rem minmax(0, 1fr); }
@media (min-width: 48rem) {
  .home .register__row { grid-template-columns: 2.5rem 16rem minmax(0, 1fr); }
}
@media (min-width: 60rem) {
  .home .register--answered .register__row {
    grid-template-columns: 2.5rem minmax(9rem, 12rem) minmax(0, 1fr) auto;
  }
}
/* The intake variant already declares minmax(0, 1fr) for its wide track and
   is left exactly as it was. */
