/* ═══════════════════════════════════════════════════════════════════════════
   HOME-2027  —  "H27"  ·  homepage editorial redesign layer
   ---------------------------------------------------------------------------
   Client brief: the homepage read like an old site — stacked light bands, small
   centred cards, timid type. This layer rebuilds the page as a sequence of
   ACTS: dark cinematic bands alternating with white editorial ones, oversized
   display type, ghost act numerals, left-anchored asymmetry, and one loud
   accent (persimmon) used sparingly.

   RULES IT KEEPS (non-negotiable brand):
     · palette locked — navy #0D1B3E · persimmon #E8593A · sky #3B86BC ·
       sage #7A9E8E. No new hues. Persimmon stays <=10% of surface.
     · type: DM Serif Display owns H1/H2 (enforced by brand-2026.css) and the
       big numerals; Inter owns everything else.
     · eyebrows keep the Round-6 corner-bracket (viewfinder) standard.
     · surfaces are white or the ice ramp — never the retired warm stone.

   LOADING: linked from home.html {% block extra_css %}, so brand-2026.css and
   ux-2027.css still load after it. Every rule is therefore scoped under `.h27`
   (and often `.h27 .pv6`) to out-specify those layers without !important —
   !important appears only where the enforcement layer uses it too.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ══ §0 · TOKENS ═════════════════════════════════════════════════════════ */

.h27 {
  /* ink ramp — deeper than the brand navy so the navy itself can sit on top
     of it as a raised plane */
  --h27-void:    #060C1E;
  --h27-ink:     #0A1330;
  --h27-navy:    #0D1B3E;
  --h27-navy-up: #14244F;

  /* accents (brand-locked) */
  --h27-coral:    #E8593A;
  --h27-coral-dk: #C94A2D;
  --h27-coral-lt: #FFAB96;
  --h27-sky:      #3B86BC;
  --h27-sky-lt:   #8BBEDD;   /* on-dark tint of sky — the ink ramp swallows #3B86BC */
  --h27-sage:     #7A9E8E;
  --h27-sage-lt:  #A9CFBE;   /* on-dark tint of sage, same reason */

  /* light surfaces — white + ice ramp only */
  --h27-paper: #FFFFFF;
  --h27-ice:   #F7FAFD;
  --h27-ice-2: #EDF4FB;
  --h27-hair:  #E3EAF2;

  /* text */
  --h27-body:  #2D2D3E;
  --h27-mute:  #5F718C;
  --h27-onink:      rgba(255,255,255,.78);
  --h27-onink-mute: rgba(255,255,255,.52);
  --h27-onink-hair: rgba(255,255,255,.13);

  /* display scale — the single biggest change from the old page */
  --h27-d0: clamp(32px, 4.7vw, 59px);   /* hero — client asked for a slightly smaller first-section headline (was 5.2vw/66px) */
  --h27-d1: clamp(30px, 4.3vw, 58px);    /* act headline — trimmed for a calmer, more professional section scale */
  --h27-d2: clamp(25px, 2.9vw, 39px);    /* sub headline */
  --h27-d3: clamp(20px, 1.9vw, 26px);    /* card headline */
  --h27-num: clamp(46px, 6.4vw, 92px);   /* number wall */

  /* rhythm */
  --h27-act:  clamp(76px, 9vw, 148px);   /* vertical act padding */
  --h27-gut:  clamp(20px, 4vw, 64px);    /* horizontal gutter */

  --h27-ease: cubic-bezier(.16, 1, .3, 1);
  --h27-out:  cubic-bezier(.22, .61, .36, 1);
}

/* ══ §1 · LAYOUT PRIMITIVES ══════════════════════════════════════════════ */

.h27 { position: relative; overflow-x: clip; }

/* the page rail — wider than the old 1140 container so the type can breathe */
.h27 .h27-edge {
  width: 100%;
  max-width: 1320px;
  margin-inline: auto;
  padding-inline: var(--h27-gut);
  position: relative;
  z-index: 2;
}
.h27 .h27-edge--tight { max-width: 1080px; }
.h27 .h27-edge--text  { max-width: 860px; }

/* ACT — the band. Dark acts stack against white ones for contrast swing. */
.h27 .h27-act {
  position: relative;
  padding-block: var(--h27-act);
  isolation: isolate;
}
.h27 .h27-act--paper { background: var(--h27-paper); }
.h27 .h27-act--ice   { background: var(--h27-ice); }
.h27 .h27-act--ink   { background: var(--h27-ink); color: var(--h27-onink); }
.h27 .h27-act--void  { background: var(--h27-void); color: var(--h27-onink); }

.h27 .h27-act--ink h2,
.h27 .h27-act--void h2,
.h27 .h27-act--ink h3,
.h27 .h27-act--void h3 { color: #fff; }
.h27 .h27-act--ink p,
.h27 .h27-act--void p { color: var(--h27-onink); }

/* hairline seam between two same-tone acts */
.h27 .h27-seam {
  border: 0; height: 1px; margin: 0;
  background: var(--h27-hair);
}
.h27 .h27-act--ink + .h27-act--ink::before,
.h27 .h27-act--void + .h27-act--ink::before,
.h27 .h27-act--ink + .h27-act--void::before {
  content: ''; position: absolute; inset-inline: var(--h27-gut); top: 0; height: 1px;
  background: var(--h27-onink-hair);
}

/* ── atmosphere on dark acts: engineered grid + slow brand aurora + grain ── */
.h27 .h27-atm { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.h27 .h27-atm::before {
  content: ''; position: absolute; inset: 0;
  background-image:
    linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px);
  background-size: 72px 72px;
  mask-image: radial-gradient(80% 70% at 50% 30%, #000 0%, transparent 84%);
  -webkit-mask-image: radial-gradient(80% 70% at 50% 30%, #000 0%, transparent 84%);
}
.h27 .h27-atm::after {
  content: ''; position: absolute; inset: -20% -10%;
  opacity: .62; filter: blur(14px);
  background:
    none;
  animation: h27-drift 34s var(--h27-ease) infinite alternate;
}
@keyframes h27-drift {
  0%   { transform: translate3d(0,0,0) scale(1); }
  50%  { transform: translate3d(2.4%,-2%,0) scale(1.06); }
  100% { transform: translate3d(-2%,2.2%,0) scale(1.03); }
}
/* film grain — kills banding on the big navy planes */
.h27 .h27-grain {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  opacity: .35; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='.34'/%3E%3C/svg%3E");
}

/* ══ §2 · ACT HEADER — ghost numeral + eyebrow + oversized headline ══════ */

.h27 .h27-head {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .82fr);
  gap: clamp(20px, 4vw, 72px);
  align-items: end;
  margin-bottom: clamp(38px, 5vw, 72px);
}
.h27 .h27-head--solo { grid-template-columns: minmax(0, 1fr); }
.h27 .h27-head--mid  { text-align: center; grid-template-columns: minmax(0,1fr); justify-items: center; }

/* the ghost act numeral — outlined, huge, parked behind the headline */
.h27 .h27-actno {
  position: absolute;
  top: clamp(-40px, -2.6vw, -14px);
  left: -.055em;
  z-index: -1;
  font-family: 'DM Serif Display', Georgia, serif;
  font-size: clamp(78px, 11.5vw, 184px);
  line-height: .8;
  letter-spacing: -.04em;
  color: transparent;
  -webkit-text-stroke: 1px rgba(13,27,62,.10);
  user-select: none;
  pointer-events: none;
}
.h27 .h27-act--ink .h27-actno,
.h27 .h27-act--void .h27-actno { -webkit-text-stroke-color: rgba(255,255,255,.10); }
.h27 .h27-head--mid .h27-actno { left: 50%; transform: translateX(-50%); }

/* eyebrow — Round-6 corner-bracket standard, rebuilt at this layer's scale */
.h27 .h27-eyebrow {
  position: relative;
  display: inline-block;
  /* As a direct grid child the eyebrow is blockified and would stretch, pulling
     its bottom-right corner tick out to the far edge of the column. */
  width: max-content; max-width: 100%;
  padding: 7px 14px;
  margin-bottom: 18px;
  font-family: 'Inter', -apple-system, sans-serif;
  font-size: 11px; font-weight: 800;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--h27-navy);
}
.h27 .h27-eyebrow::before,
.h27 .h27-eyebrow::after {
  content: ''; position: absolute; width: 11px; height: 11px;
  border-style: solid; border-color: var(--h27-coral);
}
.h27 .h27-eyebrow::before { top: 0; left: 0;     border-width: 2px 0 0 2px; }
.h27 .h27-eyebrow::after  { bottom: 0; right: 0; border-width: 0 2px 2px 0; }
.h27 .h27-act--ink .h27-eyebrow,
.h27 .h27-act--void .h27-eyebrow,
.h27 .h27-hero .h27-eyebrow { color: var(--h27-coral-lt); }
.h27 .h27-act--ink .h27-eyebrow::before, .h27 .h27-act--ink .h27-eyebrow::after,
.h27 .h27-act--void .h27-eyebrow::before, .h27 .h27-act--void .h27-eyebrow::after,
.h27 .h27-hero .h27-eyebrow::before, .h27 .h27-hero .h27-eyebrow::after { border-color: var(--h27-coral-lt); }

/* the headline itself — the loudest single change on the page */
.h27 .h27-head h2,
.h27 h2.h27-h {
  font-size: var(--h27-d1);
  line-height: 1.02;
  letter-spacing: -.02em;
  color: var(--h27-navy);
  margin: 0;
  text-wrap: balance;
}
.h27 .h27-head p,
.h27 .h27-lede {
  font-size: clamp(16px, 1.15vw, 19px);
  line-height: 1.62;
  color: var(--h27-mute);
  max-width: 46ch;
  margin: 0;
}
/* Dark acts re-assert white on the headline/lede here — §1's `.h27-act--ink h2`
   ties with `.h27-head h2` on specificity and would lose on source order. */
.h27 .h27-act--ink .h27-head h2,
.h27 .h27-act--void .h27-head h2,
.h27 .h27-act--ink h2.h27-h,
.h27 .h27-act--void h2.h27-h { color: #fff; }
.h27 .h27-act--ink .h27-lede,
.h27 .h27-act--void .h27-lede,
.h27 .h27-act--ink .h27-head p,
.h27 .h27-act--void .h27-head p { color: var(--h27-onink); }

/* accent words inside a headline */
.h27 .h27-hl { color: var(--h27-coral); }
.h27 .h27-hl-s { color: var(--h27-sky); }
.h27 .h27-act--ink .h27-hl,
.h27 .h27-act--void .h27-hl,
.h27 .h27-hero .h27-hl { color: var(--h27-coral-lt); }
/* Underscored accent — a coral rule drawn under the phrase on reveal.
   Painted as a background rather than an ::after bar so it survives a line
   break: a display headline wraps, and an absolutely-positioned bar would
   both overflow the column and only underline one line. */
.h27 .h27-mark {
  background-image: linear-gradient(var(--h27-coral), var(--h27-coral));
  background-repeat: no-repeat;
  background-position: 0 calc(100% - .05em);
  background-size: 0% 3px;
  transition: background-size 900ms var(--h27-ease) 220ms;
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
}
.h27 .x27-in .h27-mark,
.h27 .h27-mark.x27-in { background-size: 100% 3px; }

/* ══ §3 · BUTTONS — bigger, squarer, with a moving fill ══════════════════ */

.h27 .h27-btn {
  position: relative;
  display: inline-flex; align-items: center; gap: 10px;
  padding: 16px 28px;
  border: 0; border-radius: 4px;
  font-family: 'Inter', sans-serif;
  font-size: 15px; font-weight: 700; letter-spacing: -.005em;
  text-decoration: none; cursor: pointer;
  overflow: hidden; isolation: isolate;
  transition: transform 320ms var(--h27-ease), box-shadow 320ms var(--h27-ease), color 240ms linear;
}
.h27 .h27-btn > * { position: relative; z-index: 2; }
.h27 .h27-btn::before {
  content: ''; position: absolute; inset: 0; z-index: 1;
  transform: translateY(101%);
  transition: transform 460ms var(--h27-ease);
}
.h27 .h27-btn:hover { transform: translateY(-2px); }
.h27 .h27-btn:hover::before { transform: translateY(0); }
.h27 .h27-btn__a {
  display: inline-block;
  transition: transform 380ms var(--h27-ease);
}
.h27 .h27-btn:hover .h27-btn__a { transform: translateX(5px); }

.h27 .h27-btn--fill  { background: var(--h27-coral); color: #fff; box-shadow: 0 10px 30px rgba(232,89,58,.28); }
.h27 .h27-btn--fill::before { background: var(--h27-coral-dk); }
.h27 .h27-btn--fill:hover { box-shadow: 0 16px 40px rgba(232,89,58,.34); }

.h27 .h27-btn--line { background: transparent; color: var(--h27-navy); box-shadow: inset 0 0 0 1.5px rgba(13,27,62,.28); }
.h27 .h27-btn--line::before { background: var(--h27-navy); }
.h27 .h27-btn--line:hover { color: #fff; }

/* Every dark ground the outline button can land on. .h27-close is a band, not
   an act, so it has to be named here explicitly — without it the button keeps
   the light-mode navy-on-navy treatment and disappears. */
.h27 .h27-act--ink .h27-btn--line,
.h27 .h27-act--void .h27-btn--line,
.h27 .h27-close .h27-btn--line,
.h27 .h27-hero .h27-btn--line { color: #fff; box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.34); }
.h27 .h27-act--ink .h27-btn--line::before,
.h27 .h27-act--void .h27-btn--line::before,
.h27 .h27-close .h27-btn--line::before,
.h27 .h27-hero .h27-btn--line::before { background: #fff; }
.h27 .h27-act--ink .h27-btn--line:hover,
.h27 .h27-act--void .h27-btn--line:hover,
.h27 .h27-close .h27-btn--line:hover,
.h27 .h27-hero .h27-btn--line:hover { color: var(--h27-navy); }

/* quiet text link with a sliding rule */
.h27 .h27-link {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: 'Inter', sans-serif; font-size: 14px; font-weight: 700;
  color: var(--h27-sky); text-decoration: none;
  padding-bottom: 3px;
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1.5px; background-position: 0 100%; background-repeat: no-repeat;
  transition: background-size 420ms var(--h27-ease);
}
.h27 .h27-link:hover { background-size: 100% 1.5px; }
.h27 .h27-act--ink .h27-link,
.h27 .h27-act--void .h27-link,
.h27 .h27-close .h27-link,
.h27 .h27-hero .h27-link { color: var(--h27-coral-lt); }
/* the hero's "watch the tour" trigger is a <button> wearing .h27-link */
.h27 button.h27-link { font-family: 'Inter', sans-serif; padding: 11px 0; }

.h27 .h27-cta-row { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 18px; }

/* ══ §4 · HERO ═══════════════════════════════════════════════════════════ */

.h27 .h27-hero {
  position: relative;
  min-height: min(calc(100vh - 128px), 860px); /* 72px header + tightened breathing room */
  display: flex; flex-direction: column;
  padding-top: clamp(44px, 4.6vh, 62px);
  padding-bottom: 0;
  background: var(--h27-void);
  color: #fff;
  overflow: hidden;
  isolation: isolate;
}
.h27 .h27-hero__video {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  /* the footage carries the frame now — it sits under a directional scrim
     rather than a flat blanket, so it can run bright without costing legibility */
  opacity: 1;
  filter: saturate(.96) contrast(1.02);
}
.h27 .h27-hero__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  /* One flat legibility veil. This used to be a four-layer stack — a
     directional wash, a vertical seat and two brand glows — which is exactly
     the gradient look the flat pass removed. A single even veil holds the
     headline contrast across every frame of the loop. */
  background: rgba(6, 12, 30, 0.34);
}
/* the headline sits directly on moving footage — a soft shadow keeps the
   contrast ratio steady across the brightest frames of the loop */
.h27 .h27-hero__inner .h27-kicker,
.h27 .h27-hero__inner h1,
.h27 .h27-hero__sub { text-shadow: 0 2px 30px rgba(6,12,30,.82), 0 1px 4px rgba(6,12,30,.46); }
.h27 .h27-hero__inner {
  position: relative; z-index: 3; width: 100%;
  /* fill the hero and center the content block in the space above the rail —
     the grid used to sit flush against the rail, leaving all the slack as an
     empty band over the headline */
  flex: 1; display: flex; flex-direction: column;
}
.h27 .h27-hero__inner > .h27-edge {
  flex: 1; display: flex; flex-direction: column; justify-content: center;
}

.h27 .h27-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.06fr) minmax(0, .94fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
  padding-bottom: clamp(12px, 1.4vw, 18px);
}

.h27 .h27-hero .h27-kicker {
  font-family: 'Inter', sans-serif;
  font-size: clamp(13px, 1.05vw, 15px); font-weight: 600;
  color: var(--h27-onink-mute);
  margin-bottom: 14px;
}
.h27 .h27-hero h1 {
  font-size: var(--h27-d0);
  line-height: .92;
  letter-spacing: -.03em;
  color: #fff;
  margin: 0 0 18px;
  text-wrap: balance;
}
.h27 .h27-hero__sub {
  font-size: clamp(16px, 1.25vw, 20px);
  line-height: 1.58;
  color: var(--h27-onink);
  max-width: 50ch;
  margin: 0 0 20px;
}
.h27 .h27-hero .h27-cta-row { margin-bottom: 30px; }

/* live signal chip */
.h27 .h27-signal {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 8px 16px 8px 12px;
  margin-bottom: 20px;
  border-radius: 999px;
  background: rgba(255,255,255,.06);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.16);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  font-family: 'Inter', sans-serif;
  font-size: 12.5px; font-weight: 600; letter-spacing: .01em;
  color: rgba(255,255,255,.9);
}
.h27 .h27-signal i {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--h27-coral); flex: 0 0 auto;
  box-shadow: 0 0 0 0 rgba(232,89,58,.6);
  animation: h27-ping 2.6s var(--h27-ease) infinite;
}
@keyframes h27-ping {
  0%   { box-shadow: 0 0 0 0 rgba(232,89,58,.55); }
  70%  { box-shadow: 0 0 0 9px rgba(232,89,58,0); }
  100% { box-shadow: 0 0 0 0 rgba(232,89,58,0); }
}

/* ══ §4b · HERO STAGE — "the clinic, live" ═══════════════════════════════
   The old right-hand visual was a flat screenshot card borrowed from the .pv6
   layer: one plane, no motion, nothing happening. This rebuilds it as a DECK —
   back-plates in z, the app window on top, two proof cards floating off the
   glass at different depths — that tilts to the pointer and plays a short loop
   of the scribe writing the note. Self-contained: nothing here depends on .pv6.
   Palette stays locked (navy · persimmon · sky · sage); JS lives in
   home-2027.js §8 and every animation is off under reduced-motion.
   ═══════════════════════════════════════════════════════════════════════ */

.h27 .h27-hero__stage {
  position: relative;
  perspective: 1600px;
  perspective-origin: 64% 42%;
  /* the window carries a lot now — held back from full size it reads as a
     product shot rather than a wall of UI competing with the headline */
  transform: scale(.88);
}
.h27 .h27-hs { position: relative; }

/* ambient brand light behind the deck — lifts it off the video scrim */
.h27 .h27-hs__aura {
  position: absolute; inset: -20% -14% -24% -10%;
  z-index: 0; pointer-events: none;
  background:
    none;
  filter: blur(16px);
  animation: h27-hs-aura 15s ease-in-out infinite alternate;
}
@keyframes h27-hs-aura {
  from { transform: translate3d(0,0,0) scale(1);       opacity: .82; }
  to   { transform: translate3d(-2%,2%,0) scale(1.07); opacity: 1;   }
}

/* the deck — --hs-rx/--hs-ry are written by the pointer parallax */
.h27 .h27-hs__deck {
  position: relative; z-index: 1;
  transform-style: preserve-3d;
  transform: rotateY(var(--hs-ry, -8deg)) rotateX(var(--hs-rx, 3.2deg));
  transition: transform 880ms var(--h27-ease);
  will-change: transform;
}
.h27 .h27-hs.is-tilting .h27-hs__deck { transition: transform 200ms linear; }

/* stacked back-plates: the window reads as the top of a stack, not a sticker */
.h27 .h27-hs__plate {
  position: absolute; inset: 0;
  border-radius: 18px;
  border: 1px solid rgba(255,255,255,.10);
  background: rgba(20,36,79,.70);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
.h27 .h27-hs__plate--a { transform: translate3d(3.4%, 4.4%,  -64px); opacity: .70; }
.h27 .h27-hs__plate--b { transform: translate3d(6.8%, 8.8%, -128px); opacity: .38; }

/* ── the app window ───────────────────────────────────────────────────── */
.h27 .h27-hs__win {
  position: relative;
  border-radius: 18px; overflow: hidden;
  background: #0B1630;
  border: 1px solid rgba(255,255,255,.13);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.07),
    0 34px 72px -24px rgba(0,0,0,.7),
    0 8px 22px -10px rgba(0,0,0,.5);
}

/* one app bar: brand · where you are · search · session state. The old macOS
   traffic-lights + URL strip sat above a second in-app bar — two bands of
   furniture before any product. This is the single band that replaced them. */
.h27 .h27-hs__chrome {
  display: flex; align-items: center; gap: 9px;
  height: 38px; padding: 0 11px;
  background: rgba(255,255,255,.03);
  border-bottom: 1px solid rgba(255,255,255,.07);
}
.h27 .h27-hs__live {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 7px; border-radius: 999px;
  background: rgba(232,89,58,.14);
  box-shadow: inset 0 0 0 1px rgba(232,89,58,.30);
  font-family: 'Inter', sans-serif;
  font-size: 8px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--h27-coral-lt);
}
.h27 .h27-hs__live i {
  width: 5px; height: 5px; border-radius: 50%; background: var(--h27-coral);
  animation: h27-ping 2.6s var(--h27-ease) infinite;
}

/* the real screen — client capture (2026-08-08) fills the window under the
   chrome bar; width/height attrs on the img reserve the box before load so
   the deck never reflows mid-entrance */
.h27 .h27-hs__shot { background: #0B1630; }
.h27 .h27-hs__shot picture,
.h27 .h27-hs__shot img { display: block; width: 100%; height: auto; }

/* ── DEAD from here to the floats (2026-08-08): the illustrative app body
   (.h27-hs__body/rail/card/note/codes/side…) was replaced by the real
   capture above. Chrome-bar and float styles further down are still live. ── */
.h27 .h27-hs__body {
  display: grid; grid-template-columns: 54px minmax(0, 1fr);
  background: #0B1630;
}

/* module rail — labelled, so it reads as software rather than an icon strip */
.h27 .h27-hs__rail {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 9px 0 11px;
  border-right: 1px solid rgba(255,255,255,.07);
}
.h27 .h27-hs__brand {
  flex: 0 0 auto;
  width: 21px; height: 21px; border-radius: 6px;
  display: grid; place-items: center;
  background: var(--h27-navy-up);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.18);
  font-family: 'Inter', sans-serif; font-size: 10px; font-weight: 800;
  color: var(--h27-coral-lt);
}
.h27 .h27-hs__rspacer { flex: 1 1 auto; }
.h27 .h27-hs__ric {
  width: 42px; padding: 5px 0 4px; border-radius: 8px;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  color: rgba(255,255,255,.38);
}
.h27 .h27-hs__ric svg {
  width: 13px; height: 13px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.h27 .h27-hs__ric em {
  font-style: normal;
  font-family: 'Inter', sans-serif;
  font-size: 6.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
}
.h27 .h27-hs__ric.is-on {
  color: #fff;
  background: var(--h27-coral);
  box-shadow: 0 6px 16px -7px rgba(232,89,58,.95);
}

/* ── the work pane ────────────────────────────────────────────────────── */
.h27 .h27-hs__app { display: flex; flex-direction: column; min-width: 0; }

.h27 .h27-hs__crumb {
  flex: 1 1 auto; min-width: 0;
  font-family: 'Inter', sans-serif; font-size: 9.5px; font-weight: 600;
  color: rgba(255,255,255,.42);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.h27 .h27-hs__crumb i { font-style: normal; opacity: .5; margin: 0 2px; }
.h27 .h27-hs__crumb b { color: rgba(255,255,255,.88); font-weight: 700; }
.h27 .h27-hs__find {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 6px;
  height: 21px; padding: 0 5px 0 7px; border-radius: 6px;
  background: rgba(255,255,255,.05);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06);
  font-family: 'Inter', sans-serif; font-size: 9px;
  color: rgba(255,255,255,.34);
  white-space: nowrap; overflow: hidden;
}
.h27 .h27-hs__find svg {
  width: 10px; height: 10px; flex: 0 0 auto; opacity: .7;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.h27 .h27-hs__find em {
  flex: 0 0 auto;
  padding: 2px 4px; border-radius: 4px;
  background: rgba(255,255,255,.07);
  font-style: normal; font-size: 7.5px; font-weight: 700;
  color: rgba(255,255,255,.45);
}
.h27 .h27-hs__me {
  flex: 0 0 auto;
  width: 21px; height: 21px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(122,158,142,.30);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.16);
  font-family: 'Inter', sans-serif; font-size: 8px; font-weight: 800;
  color: #DCEAE2;
}

.h27 .h27-hs__pane {
  display: grid; grid-template-columns: minmax(0, 1fr) 120px;
  gap: 10px; padding: 11px;
}
.h27 .h27-hs__card {
  background: #fff; border-radius: 11px; padding: 12px;
  box-shadow: 0 10px 24px -16px rgba(0,0,0,.5);
}

/* patient header */
.h27 .h27-hs__pt { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; }
.h27 .h27-hs__av {
  width: 34px; height: 34px; flex: 0 0 auto; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--h27-navy-up);
  color: #fff;
  font-family: 'Inter', sans-serif; font-weight: 800; font-size: 11.5px;
  box-shadow: 0 0 0 2px #fff, 0 0 0 3.5px rgba(122,158,142,.55);
}
.h27 .h27-hs__ptxt { min-width: 0; }
.h27 .h27-hs__ptxt b {
  display: block;
  font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 700;
  letter-spacing: -.01em; color: var(--h27-navy);
}
.h27 .h27-hs__ptxt span {
  display: block; margin-top: 1px;
  font-family: 'Inter', sans-serif; font-size: 10px; color: var(--h27-mute);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.h27 .h27-hs__badge {
  margin-left: auto; flex: 0 0 auto;
  padding: 4px 8px; border-radius: 999px;
  background: rgba(122,158,142,.16); color: #3D6B5A;
  font-family: 'Inter', sans-serif;
  font-size: 8.5px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
}

/* demographics / coverage — one quiet line, no box: the boxed version read as
   a second card inside the card */
.h27 .h27-hs__meta {
  display: flex; align-items: center; gap: 8px;
  padding: 0 0 10px; margin-bottom: 10px;
  border-bottom: 1px solid var(--h27-hair);
  font-family: 'Inter', sans-serif; font-size: 9px; color: var(--h27-mute);
  white-space: nowrap; overflow: hidden;
}
.h27 .h27-hs__meta span { display: inline-flex; align-items: center; gap: 4px; }
.h27 .h27-hs__meta span + span { padding-left: 8px; border-left: 1px solid var(--h27-hair); }
.h27 .h27-hs__meta b { font-weight: 700; color: var(--h27-navy); }

/* S·O·A·P tabs with a sliding ink pill (JS moves --i 0..3) */
.h27 .h27-hs__nav {
  display: flex; align-items: center; gap: 9px; margin-bottom: 11px;
}
.h27 .h27-hs__cap {
  font-family: 'Inter', sans-serif;
  font-size: 8.5px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--h27-mute);
}
.h27 .h27-hs__tabs {
  position: relative; display: inline-flex; gap: 6px; flex: 0 0 auto;
  padding: 3px;
  border-radius: 9px; background: #EEF2F7;
}
.h27 .h27-hs__tabs span {
  position: relative; z-index: 1;
  width: 26px; height: 26px; border-radius: 7px;
  display: grid; place-items: center;
  font-family: 'Inter', sans-serif; font-size: 11px; font-weight: 800;
  color: var(--h27-mute);
  transition: color 300ms var(--h27-ease);
}
.h27 .h27-hs__tabs span.is-on { color: #fff; }
.h27 .h27-hs__tabink {
  position: absolute; z-index: 0; top: 3px; left: 3px;
  width: 26px; height: 26px; border-radius: 7px;
  background: var(--h27-navy);
  transform: translateX(calc(var(--i, 0) * 32px));   /* 26px pill + 6px gap */
  transition: transform 520ms var(--h27-ease);
}

/* the note the scribe is writing — real chart text, typed out with a caret.
   Fixed height (4 lines) so the card never reflows between S/O/A/P. */
.h27 .h27-hs__note {
  height: 66px; overflow: hidden;
  transition: opacity 220ms var(--h27-out), transform 220ms var(--h27-out);
}
.h27 .h27-hs__note.is-swap { opacity: 0; transform: translateY(-4px); }
.h27 .h27-hs__line {
  margin: 0;
  font-family: 'Inter', sans-serif;
  font-size: 10px; font-weight: 500; line-height: 16.5px;
  letter-spacing: -.005em;
  color: #22314E;
  white-space: nowrap; overflow: hidden;
}
.h27 .h27-hs__line.is-typing::after {
  content: ''; display: inline-block; vertical-align: -1px;
  width: 1.5px; height: 10px; margin-left: 1.5px;
  background: var(--h27-coral);
  animation: h27-hs-caret .68s steps(1, end) infinite;
}
@keyframes h27-hs-caret { 0%, 49% { opacity: 1 } 50%, 100% { opacity: 0 } }

/* codes the AI suggests once it reaches Assessment. Until then the row holds a
   "still listening" chip — absolutely placed, so the swap costs no reflow. */
.h27 .h27-hs__codes {
  position: relative; display: flex; gap: 5px;
  height: 22px; margin-top: 10px;                    /* one row, always — no reflow */
  flex-wrap: nowrap; overflow: hidden;
}
.h27 .h27-hs__code {
  display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto;
  padding: 4px 7px; border-radius: 6px;
  white-space: nowrap;                               /* never break a code in half */
  font-family: 'Inter', sans-serif; font-size: 9px; font-weight: 700;
  opacity: 0; transform: translateY(6px) scale(.96);
  transition: opacity 420ms var(--h27-ease), transform 420ms var(--h27-ease);
}
.h27 .h27-hs__code i { width: 4px; height: 4px; border-radius: 50%; background: currentColor; opacity: .7; }
.h27 .h27-hs__codes.is-in .h27-hs__code { opacity: 1; transform: none; }
.h27 .h27-hs__codes.is-in .h27-hs__code:nth-child(2) { transition-delay: 110ms; }
.h27 .h27-hs__codes.is-in .h27-hs__code:nth-child(3) { transition-delay: 220ms; }
.h27 .h27-hs__code--wait {
  position: absolute; left: 0; top: 0;
  opacity: 1; transform: none;
  background: #F1F5FA; color: var(--h27-mute); font-weight: 600;
}
.h27 .h27-hs__code--wait i { background: var(--h27-coral); opacity: 1; animation: h27-hs-blink 1.6s ease-in-out infinite; }
@keyframes h27-hs-blink { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }
.h27 .h27-hs__codes.is-in .h27-hs__code--wait { opacity: 0; transition-delay: 0ms; }
.h27 .h27-hs__code--dx  { background: rgba(13,27,62,.08);   color: var(--h27-navy); }
.h27 .h27-hs__code--cpt { background: rgba(122,158,142,.18); color: #3D6B5A; }
.h27 .h27-hs__code--ai  { background: rgba(59,134,188,.14);  color: #1F6699; }

/* scribe bar — a live waveform, not a static strip */
.h27 .h27-hs__scribe {
  display: flex; align-items: center; gap: 9px;
  margin-top: 12px; padding: 8px 10px; border-radius: 9px;
  background: rgba(232,89,58,.10);
  box-shadow: inset 0 0 0 1px rgba(232,89,58,.20);
  font-family: 'Inter', sans-serif; font-size: 10px; font-weight: 700;
  color: #B03D22;
}
.h27 .h27-hs__wave { display: flex; align-items: center; gap: 2px; height: 13px; flex: 0 0 auto; }
.h27 .h27-hs__wave i {
  width: 2px; height: 28%; border-radius: 1px; background: var(--h27-coral);
  animation: h27-hs-wave 1.05s ease-in-out infinite;
}
.h27 .h27-hs__wave i:nth-child(2) { animation-delay: .13s }
.h27 .h27-hs__wave i:nth-child(3) { animation-delay: .26s }
.h27 .h27-hs__wave i:nth-child(4) { animation-delay: .39s }
.h27 .h27-hs__wave i:nth-child(5) { animation-delay: .52s }
@keyframes h27-hs-wave { 0%, 100% { height: 28% } 50% { height: 100% } }
.h27 .h27-hs__scribe b {
  margin-left: auto;
  font-size: 9px; font-weight: 800; letter-spacing: .07em;
  color: #B03D22; opacity: .68;
  font-variant-numeric: tabular-nums;
}

/* card footer — the note ends in one decision, not a toolbar */
.h27 .h27-hs__acts {
  display: flex; align-items: center; gap: 8px;
  margin-top: 11px;
}
.h27 .h27-hs__btn {
  margin-left: auto;
  padding: 6px 11px; border-radius: 7px;
  background: #fff; box-shadow: inset 0 0 0 1px var(--h27-hair);
  font-family: 'Inter', sans-serif; font-size: 9.5px; font-weight: 700;
  color: var(--h27-mute);
}
.h27 .h27-hs__btn--p {
  background: var(--h27-navy); color: #fff; box-shadow: none;
}

/* ── schedule column — the rest of the clinic, still running. No panel fill:
      a hairline is enough to separate it from the chart. ─────────────────── */
.h27 .h27-hs__side {
  display: flex; flex-direction: column;
  padding: 1px 0 1px 11px;
  border-left: 1px solid rgba(255,255,255,.08);
}
.h27 .h27-hs__sh {
  display: flex; align-items: center; gap: 6px; margin-bottom: 9px;
  font-family: 'Inter', sans-serif;
  font-size: 8.5px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(255,255,255,.62);
}
.h27 .h27-hs__sh em {
  margin-left: auto; padding: 2px 5px; border-radius: 5px;
  background: rgba(255,255,255,.07);
  font-style: normal; font-size: 7.5px; letter-spacing: .04em;
  color: rgba(255,255,255,.5);
}
.h27 .h27-hs__q { list-style: none; margin: 0 0 auto; padding: 0; }
.h27 .h27-hs__q li {
  display: grid; grid-template-columns: minmax(0, 1fr);
  padding: 5px 6px; border-radius: 7px; margin-bottom: 2px;
}
.h27 .h27-hs__q b {
  font-family: 'Inter', sans-serif; font-size: 8.5px; font-weight: 800;
  letter-spacing: .04em; color: rgba(255,255,255,.44);
  font-variant-numeric: tabular-nums;
}
.h27 .h27-hs__q span {
  display: block; margin-top: 1px;
  font-family: 'Inter', sans-serif; font-size: 9.5px; font-weight: 600;
  color: rgba(255,255,255,.82);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.h27 .h27-hs__q i {
  display: block; margin-top: 2px;
  font-style: normal; font-family: 'Inter', sans-serif;
  font-size: 8px; font-weight: 600; color: rgba(255,255,255,.38);
}
.h27 .h27-hs__q li.is-now {
  background: rgba(232,89,58,.14);
  box-shadow: inset 0 0 0 1px rgba(232,89,58,.28);
}
.h27 .h27-hs__q li.is-now b { color: var(--h27-coral-lt); }
.h27 .h27-hs__q li.is-now i { color: rgba(255,171,150,.85); }
.h27 .h27-hs__srow {
  display: flex; align-items: center; gap: 8px;
  padding-top: 7px; margin-top: 7px;
  border-top: 1px solid rgba(255,255,255,.08);
  font-family: 'Inter', sans-serif; font-size: 8.5px; font-weight: 600;
  color: rgba(255,255,255,.42);
}
.h27 .h27-hs__srow + .h27-hs__srow { margin-top: 0; border-top: 0; padding-top: 4px; }
.h27 .h27-hs__srow b {
  margin-left: auto; font-size: 10px; font-weight: 800; color: #fff;
  font-variant-numeric: tabular-nums;
}
.h27 .h27-hs__srow b.is-ok { color: #8FD3AF; }

/* ── floating proof cards — what the note becomes ─────────────────────── */
.h27 .h27-hs__float {
  position: absolute; z-index: 3;
  padding: 11px 13px; border-radius: 13px;
  background: rgba(255,255,255,.97);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.6), 0 26px 50px -20px rgba(0,0,0,.66);
  opacity: 0;
  transition: opacity 760ms var(--h27-ease), transform 760ms var(--h27-ease);
}
/* Anchored just off the corners: any further in and they cover the schedule
   header (top) or the card footer (bottom) now that the window is shorter. */
.h27 .h27-hs__float--claim {
  top: -13%; right: -10%;
  transform: translate3d(18px, 12px, 96px);
  transition-delay: 460ms;
}
.h27 .h27-hs__float--pay {
  bottom: -13%; left: -8%;
  transform: translate3d(-18px, 14px, 62px);
  transition-delay: 660ms;
}
.h27 .h27-hs.is-in .h27-hs__float--claim { opacity: 1; transform: translate3d(0, 0, 96px); }
.h27 .h27-hs.is-in .h27-hs__float--pay   { opacity: 1; transform: translate3d(0, 0, 62px); }

/* the bob lives on an inner node so it never fights the entrance transform */
.h27 .h27-hs__bob { animation: h27-hs-bob 7.5s ease-in-out infinite; }
.h27 .h27-hs__float--pay .h27-hs__bob { animation-duration: 9s; animation-delay: -3s; }
@keyframes h27-hs-bob { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(-7px) } }

.h27 .h27-hs__float--claim .h27-hs__bob { display: flex; align-items: center; gap: 9px; }
.h27 .h27-hs__fic {
  width: 26px; height: 26px; flex: 0 0 auto; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(122,158,142,.20); color: #3D6B5A;
}
.h27 .h27-hs__fic svg {
  width: 13px; height: 13px;
  fill: none; stroke: currentColor; stroke-width: 2.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.h27 .h27-hs__ftxt b {
  display: block;
  font-family: 'Inter', sans-serif; font-size: 11.5px; font-weight: 800;
  letter-spacing: -.01em; color: var(--h27-navy);
}
.h27 .h27-hs__ftxt span {
  display: block; margin-top: 1px;
  font-family: 'Inter', sans-serif; font-size: 9.5px; color: var(--h27-mute);
}

.h27 .h27-hs__fhead {
  display: flex; align-items: center; gap: 7px;
  font-family: 'Inter', sans-serif;
  font-size: 8.5px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--h27-mute);
}
.h27 .h27-hs__fhead em {
  font-style: normal; font-size: 8.5px; font-weight: 800;
  color: #3D6B5A; background: rgba(122,158,142,.18);
  padding: 2px 5px; border-radius: 5px;
}
.h27 .h27-hs__fnum {
  display: block; margin: 5px 0 7px;
  font-family: 'DM Serif Display', Georgia, serif; font-weight: 400;
  font-size: 24px; line-height: 1; letter-spacing: -.02em;
  color: var(--h27-navy);
  font-variant-numeric: tabular-nums;
}
.h27 .h27-hs__spark { display: flex; align-items: flex-end; gap: 3px; height: 22px; }
.h27 .h27-hs__spark i {
  width: 5px; border-radius: 2px 2px 1px 1px;
  height: var(--h, 40%);
  background: rgba(13,27,62,.16);
}
.h27 .h27-hs__spark i:last-child { background: var(--h27-coral); }

/* ── reduced motion: the composition holds, nothing moves ─────────────── */
@media (prefers-reduced-motion: reduce) {
  .h27 .h27-hs__aura,
  .h27 .h27-hs__bob,
  .h27 .h27-hs__wave i,
  .h27 .h27-hs__live i { animation: none; }
  .h27 .h27-hs__deck,
  .h27 .h27-hs__note,
  .h27 .h27-hs__line,
  .h27 .h27-hs__code,
  .h27 .h27-hs__tabink,
  .h27 .h27-hs__float { transition: none; }
  .h27 .h27-hs__line.is-typing::after { display: none; }
}

/* hero foot rail — replaces the old standalone features-bar strip */
.h27 .h27-hero__rail {
  position: relative; z-index: 3;
  border-top: 1px solid rgba(255,255,255,.14);
  /* the scrim under the rail is lighter now that the video runs bright — the
     glass carries a touch more tint so the stat numbers keep their contrast */
  background: rgba(6,12,30,.58);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
}
.h27 .h27-rail-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.h27 .h27-rail-cell {
  padding: 19px clamp(14px, 2vw, 30px);
  border-left: 1px solid rgba(255,255,255,.11);
}
.h27 .h27-rail-cell:first-child { border-left: 0; padding-left: 0; }
.h27 .h27-rail-cell b {
  display: block;
  font-family: 'DM Serif Display', Georgia, serif;
  font-weight: 400;
  font-size: clamp(26px, 2.6vw, 40px);
  line-height: 1; letter-spacing: -.02em;
  color: #fff;
  margin-bottom: 7px;
}
.h27 .h27-rail-cell span {
  display: block;
  font-family: 'Inter', sans-serif;
  font-size: 11.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--h27-onink-mute);
  line-height: 1.4;
}
.h27 .h27-rail-cell--accent b { color: var(--h27-coral-lt); }

/* hero video toggle */
.h27 .h27-hero__vtoggle {
  position: absolute; z-index: 4; right: var(--h27-gut); top: clamp(96px, 12vh, 150px);
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.34);
  background: rgba(6,12,30,.5); color: #fff;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: background 260ms var(--h27-ease), transform 260ms var(--h27-ease);
}
.h27 .h27-hero__vtoggle:hover { background: rgba(232,89,58,.9); transform: scale(1.06); }

/* ══ §5 · TICKER — clinic names, slim, dark ══════════════════════════════ */

.h27 .h27-ticker {
  background: var(--h27-void);
  border-top: 1px solid rgba(255,255,255,.10);
  border-bottom: 1px solid rgba(255,255,255,.10);
  padding: 18px 0;
  overflow: hidden;
  position: relative;
}
.h27 .h27-ticker::before,
.h27 .h27-ticker::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: 16vw; z-index: 2; pointer-events: none;
}
.h27 .h27-ticker::before { left: 0;  background: none; }
.h27 .h27-ticker::after  { right: 0; background: none; }
.h27 .h27-ticker__track {
  display: flex; align-items: center; gap: clamp(28px, 4vw, 56px);
  width: max-content;
  animation: h27-scroll 46s linear infinite;
}
.h27 .h27-ticker:hover .h27-ticker__track { animation-play-state: paused; }
.h27 .h27-ticker__item {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: 'Inter', sans-serif;
  font-size: 13px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(255,255,255,.46);
  white-space: nowrap;
  transition: color 300ms var(--h27-ease);
}
.h27 .h27-ticker__item::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: var(--h27-coral); opacity: .7; flex: 0 0 auto;
}
.h27 .h27-ticker__item:hover { color: #fff; }
@keyframes h27-scroll { from { transform: translate3d(0,0,0); } to { transform: translate3d(-50%,0,0); } }

/* ══ §6 · ACT INDEX — the sticky chapter rail ════════════════════════════
   Sticks directly under the fixed header for the whole page and reports where
   the reader is. It has three faces:

     · desktop — a chapter rail with a sliding marker whose coral tick fills
       with progress *through the lit chapter*, and edge chevrons that appear
       only on the side the rail can still scroll toward.
     · phone — the rail collapses into one compact picker button that opens a
       bottom sheet listing every chapter. Twelve chips behind a swipe is a
       list nobody reads; one tap to the whole list is.
     · sticking — the bar's height is identical free and stuck, deliberately.
       A condense-on-stick resizes a box that is still in flow, so the page
       below it jumps at the exact pixel it sticks, and on a trackpad that
       jump can bounce the bar in and out of its own stuck state.

   Two rules make the sticky work at all — (1) nothing between it and <body>
   may be a scroll container (see the overflow-x:clip note in styles.css), and
   (2) it is a child of `.h27`, whose box spans the whole page, so the sticky
   range covers every act.

   No page-progress line here on purpose — ux-2027.js §9 already puts a scroll
   beam at the top of the viewport, and a second one 72px below it reads as two
   competing indicators. The marker tick measures the chapter, not the page.

   home-2027.js §2 owns .is-stuck / .is-spied / .has-pick, the data-of overflow
   state, the marker offsets, the chapter fill and the sheet.               */

.h27 {
  --h27-idx-bg:       rgba(255,255,255,.86);
  --h27-idx-bg-stuck: rgba(255,255,255,.94);
  --h27-idx-hair:     var(--h27-hair);
  /* how far the cut edge of the rail dissolves. Wide enough to cover the edge
     chevron (30px at an 8px inset), or the button sits on top of a label that
     is still at full contrast and the two fight. */
  --h27-idx-fade:     46px;
}

.h27 .h27-index {
  position: sticky; z-index: 40;
  top: var(--header-height, 72px);
  background: var(--h27-idx-bg);
  -webkit-backdrop-filter: blur(20px) saturate(180%); backdrop-filter: blur(20px) saturate(180%);
  border-bottom: 1px solid var(--h27-idx-hair);
  transition: background 340ms var(--h27-ease), box-shadow 420ms var(--h27-ease),
              border-color 340ms var(--h27-ease);
}
/* stuck = the reader is inside the sequence: the bar lifts off the page so it
   reads as chrome over the acts rather than a band between them */
.h27 .h27-index.is-stuck {
  background: var(--h27-idx-bg-stuck);
  border-bottom-color: rgba(13,27,62,.10);
  box-shadow: 0 1px 0 rgba(13,27,62,.05), 0 14px 34px -22px rgba(13,27,62,.55);
}

.h27 .h27-index__wrap {
  position: relative;
  max-width: 1320px; margin-inline: auto;
}

.h27 .h27-index__inner {
  position: relative;
  display: flex; gap: 2px; align-items: stretch;
  padding-inline: var(--h27-gut);
  overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: none;
  scroll-behavior: smooth;
}
.h27 .h27-index__inner::-webkit-scrollbar { display: none; }

/* ── the sliding marker ────────────────────────────────────────────────────
   One element for all twelve chapters, so the active state travels instead of
   blinking on and off. It rides the scroller's content box, which is exactly
   its containing block, so it stays put while the rail scrolls under it. */
.h27 .h27-index__marker {
  position: absolute; z-index: 0; pointer-events: none;
  top: 6px; bottom: 8px; left: 0; width: var(--h27-mw, 0px);
  border-radius: 12px;
  background: #fff;
  box-shadow: inset 0 0 0 1px rgba(13,27,62,.07),
              0 1px 1px rgba(13,27,62,.04),
              0 6px 16px -10px rgba(13,27,62,.5);
  opacity: 0;
  transform: translate3d(var(--h27-mx, 0px), 0, 0);
  transition: transform 560ms var(--h27-ease), width 560ms var(--h27-ease),
              opacity 260ms linear;
  will-change: transform, width;
}
.h27 .h27-index.is-spied .h27-index__marker { opacity: 1; }
/* The persimmon tick, landing on the bar's own bottom rule. Its coral half is
   how far the reader is through the lit chapter (--h27-mp, set per frame by
   the spy) — a hard gradient stop rather than a transform, so it can update
   every scroll frame without fighting the marker's own 560ms travel. */
.h27 .h27-index__marker::after {
  content: ''; position: absolute; left: 13px; right: 13px; bottom: -8px; height: 2px;
  border-radius: 2px 2px 0 0;
  background: linear-gradient(90deg,
              var(--h27-coral) var(--h27-mp, 100%),
              rgba(232,89,58,.22) var(--h27-mp, 100%));
}

.h27 .h27-index a {
  position: relative; z-index: 1;
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 15px 14px;
  font-family: 'Inter', sans-serif;
  font-size: 12.5px; font-weight: 600; letter-spacing: -.005em;
  color: var(--h27-mute); text-decoration: none;
  white-space: nowrap;
  border-radius: 12px;
  transition: color 260ms var(--h27-ease), background 220ms var(--h27-ease);
}
.h27 .h27-index a em {
  font-style: normal;
  font-variant-numeric: tabular-nums;
  font-size: 10px; font-weight: 800; letter-spacing: .08em;
  /* platinum-theme.css §25a: the old rgba(13,27,62,.34) measured 2.13:1, the
     worst text on the site. That fix could never reach the homepage — this
     file loads after the theme and re-declared the numerals at equal
     specificity — so it is carried here rather than left to be overridden. */
  color: var(--h27-mute);
  transition: color 260ms var(--h27-ease);
}
.h27 .h27-index a:hover { color: var(--h27-navy); background: rgba(13,27,62,.035); }
.h27 .h27-index a:hover em { color: var(--h27-body); }
.h27 .h27-index a.is-active:hover { background: transparent; }
.h27 .h27-index a:focus-visible {
  outline: 2px solid var(--h27-coral); outline-offset: -3px;
}
.h27 .h27-index a.is-active { color: var(--h27-navy); }
.h27 .h27-index a.is-active em { color: var(--h27-navy); }   /* §25a: 3.18 → 15.2 */

/* ── overflow affordances ──────────────────────────────────────────────────
   data-of carries which way the rail can still travel ("l", "r" or "lr"), so
   the fade and the chevron only ever appear on a side that has something
   behind it. A permanent fade on a rail that fits reads as a rendering bug. */
.h27 .h27-index[data-of~="l"] .h27-index__inner {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--h27-idx-fade), #000 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 var(--h27-idx-fade), #000 100%);
}
.h27 .h27-index[data-of~="r"] .h27-index__inner {
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--h27-idx-fade)), transparent 100%);
          mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - var(--h27-idx-fade)), transparent 100%);
}
.h27 .h27-index[data-of~="l"][data-of~="r"] .h27-index__inner {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--h27-idx-fade),
                      #000 calc(100% - var(--h27-idx-fade)), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 var(--h27-idx-fade),
                      #000 calc(100% - var(--h27-idx-fade)), transparent 100%);
}

.h27 .h27-index__edge {
  position: absolute; top: 50%; z-index: 3;
  width: 30px; height: 30px; margin-top: -15px; padding: 0;
  display: grid; place-items: center;
  border: 0; border-radius: 50%; cursor: pointer;
  color: var(--h27-navy);
  background: rgba(255,255,255,.94);
  box-shadow: inset 0 0 0 1px rgba(13,27,62,.08), 0 4px 14px -6px rgba(13,27,62,.45);
  opacity: 0; visibility: hidden; transform: scale(.82);
  transition: opacity 220ms var(--h27-ease), transform 280ms var(--h27-ease),
              visibility 0s linear 220ms, background 200ms linear;
}
.h27 .h27-index__edge--l { left: 8px; }
.h27 .h27-index__edge--r { right: 8px; }
.h27 .h27-index[data-of~="l"] .h27-index__edge--l,
.h27 .h27-index[data-of~="r"] .h27-index__edge--r {
  opacity: 1; visibility: visible; transform: scale(1); transition-delay: 0s;
}
.h27 .h27-index__edge:hover { background: #fff; }
.h27 .h27-index__edge:active { transform: scale(.92); }
.h27 .h27-index__edge:focus-visible { outline: 2px solid var(--h27-coral); outline-offset: 2px; }
.h27 .h27-index__edge svg { width: 15px; height: 15px; }

/* ── the phone picker ──────────────────────────────────────────────────────
   .has-pick is set by the script, so with JS off the rail simply stays a rail
   and the page keeps a working table of contents. */
.h27 .h27-index__pick {
  display: none;   /* -> flex inside the phone breakpoint, once .has-pick is on */
  width: 100%; padding: 11px var(--h27-gut); border: 0; background: transparent;
  align-items: center; gap: 11px; cursor: pointer; text-align: left;
  font-family: 'Inter', sans-serif; color: var(--h27-navy);
  -webkit-tap-highlight-color: transparent;
}
.h27 .h27-index__pick:focus-visible { outline: 2px solid var(--h27-coral); outline-offset: -3px; }
.h27 .h27-index__pick:active { background: rgba(13,27,62,.03); }
/* the number sits in a ring that fills with page position — the phone's
   equivalent of the desktop marker tick */
.h27 .h27-index__dial {
  position: relative; flex: 0 0 auto;
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  background:
    conic-gradient(var(--h27-coral) var(--h27-pp, 0%), rgba(13,27,62,.11) 0);
}
.h27 .h27-index__dial::before {
  content: ''; position: absolute; inset: 2.5px; border-radius: 50%; background: #fff;
}
.h27 .h27-index__dial em {
  position: relative; font-style: normal;
  font-size: 10.5px; font-weight: 800; letter-spacing: .02em;
  font-variant-numeric: tabular-nums; color: var(--h27-navy);
}
.h27 .h27-index__pickmain { min-width: 0; flex: 1 1 auto; }
.h27 .h27-index__picklabel {
  display: block; font-size: 13.5px; font-weight: 650; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.h27 .h27-index__pickmeta {
  display: block; margin-top: 1px;
  font-size: 10px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--h27-mute);
}
.h27 .h27-index__pickchev {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; color: var(--h27-mute);
  background: rgba(13,27,62,.05);
}
.h27 .h27-index__pickchev svg { width: 14px; height: 14px; }

@media (max-width: 760px) {
  .h27 .h27-index.has-pick .h27-index__inner,
  .h27 .h27-index.has-pick .h27-index__edge { display: none; }
  .h27 .h27-index.has-pick .h27-index__pick { display: flex; }
}
@media (max-width: 640px) {
  .h27 .h27-index a { padding: 13px 11px; font-size: 12px; gap: 6px; }
  .h27 .h27-index__marker { top: 5px; bottom: 7px; }
  .h27 .h27-index__marker::after { left: 10px; right: 10px; bottom: -7px; }
}

/* ── the chapter sheet ─────────────────────────────────────────────────────
   Lives on <body>, not inside the bar: the bar carries a backdrop-filter,
   which makes it a containing block for fixed descendants, so a sheet nested
   inside it would be pinned to the bar instead of the viewport. */
.h27-idxsheet {
  position: fixed; inset: 0;
  /* over the chat widget and the scroll beams, both of which sit at 9999 — it
     is a modal, so nothing may float in front of it or stay tappable behind */
  z-index: 10010;
  display: flex; flex-direction: column; justify-content: flex-end;
}
.h27-idxsheet[hidden] { display: none; }
.h27-idxsheet__scrim {
  position: absolute; inset: 0;
  background: rgba(9,17,38,.42);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  opacity: 0; transition: opacity 280ms cubic-bezier(.16,1,.3,1);
}
.h27-idxsheet__panel {
  position: relative;
  max-height: min(72vh, 620px);
  display: flex; flex-direction: column;
  background: #fff;
  border-radius: 22px 22px 0 0;
  box-shadow: 0 -18px 60px -20px rgba(9,17,38,.55);
  padding: 8px 0 max(10px, env(safe-area-inset-bottom));
  transform: translate3d(0, 100%, 0);
  transition: transform 380ms cubic-bezier(.16,1,.3,1);
}
/* the panel takes focus on open so the dialog is announced and Shift+Tab has
   somewhere to wrap from; it is a container, not a control, so it gets no ring */
.h27-idxsheet__panel:focus,
.h27-idxsheet__panel:focus-visible { outline: none; }
.h27-idxsheet.is-open .h27-idxsheet__scrim { opacity: 1; }
.h27-idxsheet.is-open .h27-idxsheet__panel { transform: translate3d(0,0,0); }

.h27-idxsheet__grip {
  width: 38px; height: 4px; border-radius: 4px; margin: 4px auto 10px;
  background: rgba(13,27,62,.16);
}
.h27-idxsheet__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; padding: 0 20px 10px;
  border-bottom: 1px solid var(--h27-hair, #E3EAF2);
}
.h27-idxsheet__title {
  margin: 0; font-family: 'Inter', sans-serif;
  font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--h27-mute, #5F718C);
}
.h27-idxsheet__count {
  font-family: 'Inter', sans-serif; font-size: 11px; font-weight: 700;
  font-variant-numeric: tabular-nums; color: rgba(13,27,62,.38);
}
.h27-idxsheet__list {
  /* min-height:0 or the flex item refuses to shrink and the list runs off the
     bottom of the panel instead of scrolling inside it */
  min-height: 0;
  overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
  padding: 6px 10px 4px;
}
.h27-idxsheet__row {
  display: flex; align-items: center; gap: 13px;
  padding: 12px 12px; border-radius: 13px;
  font-family: 'Inter', sans-serif; font-size: 14.5px; font-weight: 600;
  letter-spacing: -.01em; color: var(--h27-navy, #0D1B3E); text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.h27-idxsheet__row em {
  flex: 0 0 auto; width: 27px; height: 27px; border-radius: 9px;
  display: grid; place-items: center;
  font-style: normal; font-size: 10.5px; font-weight: 800; letter-spacing: .02em;
  font-variant-numeric: tabular-nums;
  background: rgba(13,27,62,.055); color: rgba(13,27,62,.5);
  transition: background 200ms linear, color 200ms linear;
}
.h27-idxsheet__row span { flex: 1 1 auto; }
.h27-idxsheet__row svg { flex: 0 0 auto; width: 17px; height: 17px; color: var(--h27-coral, #E8593A); opacity: 0; }
.h27-idxsheet__row:active { background: rgba(13,27,62,.04); }
.h27-idxsheet__row.is-active { background: rgba(232,89,58,.07); }
.h27-idxsheet__row.is-active em { background: var(--h27-coral, #E8593A); color: #fff; }
.h27-idxsheet__row.is-active svg { opacity: 1; }
.h27-idxsheet__row:focus-visible { outline: 2px solid var(--h27-coral, #E8593A); outline-offset: -2px; }

/* same lock the mobile menu uses in main.js §6, as a class so the two never
   overwrite each other's inline style */
body.h27-idxsheet-open { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .h27 .h27-index__marker,
  .h27 .h27-index__edge { transition: opacity 160ms linear, visibility 0s; }
  .h27 .h27-index__inner { scroll-behavior: auto; }
  .h27-idxsheet__panel, .h27-idxsheet__scrim { transition: none; }
}

/* Landing offset for *browser-driven* fragment navigation — opening the page on
   a #hash, or a link to a chapter from elsewhere. Those land via html's
   scroll-padding-top, which ux-2027.css already sets to header + the 16px
   default --x27-anchor-pad = 88px; this margin adds the rest of the bar's 52px
   so the chapter clears it rather than sitting behind it. Clicks on the rail
   itself never take this path — main.js §10 preventDefaults every in-page
   anchor, so home-2027.js §2 scrolls those to a measured position instead. */
.h27 .x27-anchor { scroll-margin-top: 46px; }

/* ══ §7 · ACT 01 · THE PROBLEM — the big five-versus-one split ═══════════ */

.h27 .h27-split {
  display: grid;
  grid-template-columns: minmax(0,1fr) minmax(0,1fr);
  border-top: 1px solid var(--h27-hair);
  border-bottom: 1px solid var(--h27-hair);
}
.h27 .h27-split__side {
  position: relative;
  padding: clamp(30px, 3.4vw, 52px) clamp(22px, 3vw, 48px) clamp(34px, 4vw, 56px);
  border-left: 1px solid var(--h27-hair);
  transition: background 520ms var(--h27-ease);
}
.h27 .h27-split__side:first-child { border-left: 0; }
.h27 .h27-split__side--now:hover  { background: rgba(13,27,62,.025); }
.h27 .h27-split__side--new { background: var(--h27-ink); color: #fff; }
.h27 .h27-split__side--new::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background: none;
}
.h27 .h27-split__side--new > * { position: relative; z-index: 1; }

/* the giant counter numeral that carries the argument */
.h27 .h27-bignum {
  display: flex; align-items: baseline; gap: 14px;
  margin-bottom: 20px;
}
.h27 .h27-bignum b {
  font-family: 'DM Serif Display', Georgia, serif; font-weight: 400;
  font-size: clamp(64px, 9vw, 132px);
  line-height: .82; letter-spacing: -.045em;
  color: var(--h27-navy);
}
.h27 .h27-split__side--new .h27-bignum b { color: var(--h27-coral-lt); }
.h27 .h27-bignum span {
  font-family: 'Inter', sans-serif;
  font-size: clamp(13px, 1.1vw, 16px); font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--h27-mute);
  max-width: 17ch; line-height: 1.35;
}
.h27 .h27-split__side--new .h27-bignum span { color: var(--h27-onink-mute); }

.h27 .h27-split__tag {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: 'Inter', sans-serif;
  font-size: 11px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--h27-mute);
  margin-bottom: 18px;
}
.h27 .h27-split__side--new .h27-split__tag { color: var(--h27-coral-lt); }
.h27 .h27-split__side p {
  font-size: clamp(15px, 1.1vw, 17px); line-height: 1.72;
  color: var(--h27-body); margin: 0;
  max-width: 44ch;
}
.h27 .h27-split__side--new p { color: var(--h27-onink); }

/* the stack of broken tools vs the single platform */
.h27 .h27-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 24px; }
.h27 .h27-chip {
  font-family: 'Inter', sans-serif;
  font-size: 12px; font-weight: 600;
  padding: 7px 13px; border-radius: 3px;
  color: var(--h27-mute);
  box-shadow: inset 0 0 0 1px var(--h27-hair);
  background: #fff;
}
.h27 .h27-chip--dead { text-decoration: line-through; opacity: .68; }
.h27 .h27-split__side--new .h27-chip {
  background: rgba(255,255,255,.06); color: #fff;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.18);
}

/* the calculator strip beneath the split */
.h27 .h27-strip {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 18px 28px;
  padding: 26px clamp(22px, 3vw, 48px);
  border: 1px solid var(--h27-hair);
  border-top: 0;
  background: var(--h27-ice);
}
.h27 .h27-strip p {
  margin: 0; font-family: 'Inter', sans-serif;
  font-size: clamp(15px, 1.2vw, 18px); font-weight: 600; color: var(--h27-navy);
}
/* same footing on an ink band — Act 02 closes the way Act 01 does */
.h27 .h27-strip--ink {
  border-color: rgba(255,255,255,.13);
  background: rgba(255,255,255,.035);
  position: relative; z-index: 1;
}
.h27 .h27-strip--ink p { color: #fff; }

/* ══ §8 · ACT 02 · RUN & GROW — two welded system panels ═════════════════
   Carries Act 01's split treatment onto the ink ground: one hairline frame,
   two surfaces inside it, a numbered hairline matrix in each, and the
   calculator-strip footing underneath. Previously the two rows floated free
   on the band with a 62px dead gap either side of the "+", which read as an
   unfinished list rather than a system. The "+" now sits *on* the seam
   between the two panels, so the weld is literal.

   Local hairline token so the frame, the seam and the cell dividers can
   never drift apart. */
.h27 .h27-os {
  --os-hair: rgba(255,255,255,.13);
  position: relative;
  border-top: 1px solid var(--os-hair);
  border-bottom: 1px solid var(--os-hair);
}

/* each half is a surface, not a floating row — Run reads neutral, Grow is
   lifted and warmed, the same neutral-vs-accented asymmetry Act 01 uses */
.h27 .h27-os__row {
  position: relative;
  padding: clamp(30px, 3.4vw, 50px) clamp(20px, 2.6vw, 40px) clamp(30px, 3.4vw, 46px);
  transition: background 520ms var(--h27-ease);
}
.h27 .h27-os__row--grow {
  background: rgba(255,255,255,.028);
  border-top: 1px solid var(--os-hair);
}
.h27 .h27-os__row::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
}
.h27 .h27-os__row--run::before  { background: none; }
.h27 .h27-os__row--grow::before { background: none; }
.h27 .h27-os__row > * { position: relative; z-index: 1; }

/* the word sits on its own line so the capability matrix below can use the
   whole rail — at half-rail the labels wrapped three deep and the trailing
   cells left a ragged hole on the right */
.h27 .h27-os__head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: clamp(20px, 3vw, 56px);
  margin-bottom: clamp(22px, 2.4vw, 32px);
}

/* left — the identifier: tag over the word at display scale */
/* block, not inline-block — the word below is an inline-block (it carries the
   accent rule) and the two would share a line box */
/* the leading tick mirrors Act 01's ✕/✓ tags without borrowing a glyph that
   already means pass/fail on this page */
.h27 .h27-os__tag {
  display: flex; align-items: center; gap: 9px; width: max-content;
  font-family: 'Inter', sans-serif;
  font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  margin-bottom: 14px;
}
.h27 .h27-os__tag::before {
  content: ''; width: 16px; height: 2px; background: currentColor; opacity: .75;
}
.h27 .h27-os__row--run  .h27-os__tag { color: #8BBEDD; }
.h27 .h27-os__row--grow .h27-os__tag { color: var(--h27-coral-lt); }

.h27 .h27-os__word {
  position: relative; display: inline-block;
  font-family: 'DM Serif Display', Georgia, serif !important;
  font-weight: 400;
  /* dialled back from clamp(56px, 8.2vw, 124px) — at 124px the word read as a
     billboard rather than a section identifier and dwarfed the matrix below;
     this holds the hierarchy at a professional display scale */
  font-size: clamp(38px, 3.8vw, 60px);
  line-height: .95; letter-spacing: -.02em;
  color: #fff; margin: 0;
}
/* the accent rule draws itself under the word as the panel reveals */
.h27 .h27-os__word::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -.18em; height: 2px;
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 900ms var(--h27-ease) 240ms;
}
.h27 .h27-os__row--run  .h27-os__word::after { background: var(--h27-sky); }
.h27 .h27-os__row--grow .h27-os__word::after { background: var(--h27-coral); }
.h27 .h27-os.x27-in .h27-os__row--grow .h27-os__word::after { transition-delay: 420ms; }
.h27 .h27-os.x27-in .h27-os__word::after { transform: scaleX(1); }

.h27 .h27-os__say {
  margin: 0 0 .5em;
  font-family: 'Inter', sans-serif;
  font-size: clamp(15px, 1.15vw, 17px); line-height: 1.55; color: var(--h27-onink-mute);
  max-width: 34ch;
}

/* the capability matrix — auto-fit collapses the unused tracks, so Run's five
   and Grow's four both fill the rail edge to edge instead of leaving a hole */
.h27 .h27-os__cells {
  display: grid;
  /* 150px min keeps all five (and all four) tracks on one line down to the
     900px breakpoint — auto-fit collapses the spares, so both rows stay
     complete instead of dropping a lone orphan cell onto a second line */
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  border-top: 1px solid var(--os-hair);
}
/* dividers instead of a column gap: the cells now read as one ruled matrix,
   the way Act 01's two sides are ruled apart rather than spaced apart */
.h27 .h27-os__cell {
  position: relative;
  display: flex; flex-direction: column;
  padding: 20px clamp(14px, 1.5vw, 22px) 26px;
  border-left: 1px solid var(--os-hair);
  transition: background 300ms var(--h27-ease);
}
.h27 .h27-os__cell:first-child { border-left: 0; padding-left: 0; }
.h27 .h27-os__cell::before {
  content: ''; position: absolute; top: -1px; left: 0; right: 0; height: 2px;
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 520ms var(--h27-ease);
}
.h27 .h27-os__row--run  .h27-os__cell::before { background: var(--h27-sky); }
.h27 .h27-os__row--grow .h27-os__cell::before { background: var(--h27-coral); }
.h27 .h27-os__cell:hover { background: rgba(255,255,255,.04); }
.h27 .h27-os__cell:hover::before { transform: scaleX(1); }

/* the spec-sheet index — the editorial numeral Act 01 makes its whole
   argument out of, dropped to a quiet register so it counts the platform
   without competing with the word */
.h27 .h27-os__no {
  position: absolute; top: 22px; right: clamp(14px, 1.5vw, 22px);
  font-family: 'Inter', sans-serif;
  font-size: 10px; font-weight: 800; letter-spacing: .1em;
  color: rgba(255,255,255,.28);
  transition: color 300ms var(--h27-ease);
}
.h27 .h27-os__row--run  .h27-os__cell:hover .h27-os__no { color: var(--h27-sky); }
.h27 .h27-os__row--grow .h27-os__cell:hover .h27-os__no { color: var(--h27-coral-lt); }

.h27 .h27-os__label {
  font-family: 'Inter', sans-serif;
  font-size: clamp(14px, 1.05vw, 15.5px); font-weight: 600; line-height: 1.4;
  color: rgba(255,255,255,.86);
  transition: color 300ms var(--h27-ease);
}
.h27 .h27-os__cell:hover .h27-os__label { color: #fff; }

.h27 .h27-os__tile {
  width: 40px; height: 40px; border-radius: 9px; margin-bottom: 15px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.07);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.13);
  transition: transform 420ms var(--h27-out), background 320ms var(--h27-ease), box-shadow 320ms var(--h27-ease);
}
.h27 .h27-os__tile .ico { width: 18px; height: 18px; font-size: 18px; color: #fff; }
.h27 .h27-os__cell:hover .h27-os__tile { transform: translateY(-2px); }
.h27 .h27-os__row--run  .h27-os__cell:hover .h27-os__tile { background: rgba(59,134,188,.28); box-shadow: inset 0 0 0 1px rgba(59,134,188,.5); }
.h27 .h27-os__row--grow .h27-os__cell:hover .h27-os__tile { background: rgba(232,89,58,.26); box-shadow: inset 0 0 0 1px rgba(232,89,58,.5); }

/* the "+" weld — zero-height, so the badge centres exactly on the seam the
   Grow panel's top border already draws. No margins: the two panels' own
   padding is the only air, which is what removed the 120px dead band. */
.h27 .h27-os__weld {
  position: relative; z-index: 2;
  height: 0;
  display: flex; align-items: center; justify-content: center;
}
.h27 .h27-os__weld span {
  position: relative;
  width: 50px; height: 50px; border-radius: 50%;
  background: var(--h27-navy-up);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.2), 0 12px 30px rgba(0,0,0,.4);
}
.h27 .h27-os__weld span::before {
  content: ''; position: absolute; inset: -8px; border-radius: 50%;
  border: 1px solid rgba(255,171,150,.32);
  animation: h27-ring 3.6s var(--h27-ease) infinite;
}
/* the mark is drawn, not typed. A text "+" centred with flex is centred on its
   line box, and Inter puts the glyph on the math axis with a trailing side
   bearing — so it landed high and left of the disc's true centre. Two gradient
   bars on one pseudo sit on the geometric middle in both axes, at any size and
   whatever font the fallback chain ends up serving. */
.h27 .h27-os__weld span::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  width: 17px; height: 17px; transform: translate(-50%, -50%);
  background:
    linear-gradient(#fff, #fff) center / 17px 1.5px no-repeat,
    linear-gradient(#fff, #fff) center / 1.5px 17px no-repeat;
}
@keyframes h27-ring {
  0%   { transform: scale(.8); opacity: 0; }
  40%  { opacity: .9; }
  100% { transform: scale(1.3); opacity: 0; }
}

/* ══ §8b · INTERLUDE · THE AI LAYER ══════════════════════════════════════
   Replaces `.h27-foundation`, the strip that used to be welded under the two
   planes — at that scale the AI claim read as a footnote to Act 02. It is now
   its own void band: claim on the left, an "ask" console on the right.

   The console is CSS-only (no JS hooks beyond the shared [data-h27] reveal),
   so with motion off it settles into a static, legible panel showing the
   first prompt — every loop's 100% keyframe lands back on its start state. */

/* The band sits directly under Act 02 (--ink #0A1330); at the void tone
   #060C1E the two read as one continuous dark run. It lifts to the brand navy
   plane instead — same locked palette, a clear step LIGHTER than the act above
   — so the interlude reads as its own band. A persimmon-tinted top edge does
   the actual dividing line. */
.h27 .h27-act--void.h27-ail {
  background:
    var(--h27-navy-up);
}
.h27 .h27-act--ink + .h27-act--void.h27-ail::before {
  height: 2px;
  background: rgba(232,89,58,.55);
}

.h27 .h27-ail__grid {
  display: grid;
  grid-template-columns: minmax(0, .94fr) minmax(0, 1.06fr);
  gap: clamp(34px, 5vw, 78px);
  align-items: center;
}
/* the band runs a step below the act headline scale — it is an interlude, and
   the sentence is long enough that --h27-d1 would swallow the column */
.h27 .h27-ail h2.h27-h {
  font-size: clamp(26px, 2.8vw, 37px);
  line-height: 1.1; letter-spacing: -.022em;
}
.h27 .h27-ail .h27-lede { margin-top: 18px; max-width: 40ch; }

/* the three "how it is built in" facts — hairline list with a coral tick rail */
.h27 .h27-ail__facts { list-style: none; margin: clamp(26px, 3vw, 36px) 0 0; padding: 0; }
.h27 .h27-ail__facts li {
  position: relative;
  padding: 15px 0 15px 24px;
  border-top: 1px solid var(--h27-onink-hair);
  font-family: 'Inter', sans-serif;
}
.h27 .h27-ail__facts li:last-child { border-bottom: 1px solid var(--h27-onink-hair); }
/* 2px, not a hairline — at 1px the rail lands on a half pixel on some rows and
   antialiases to grey, so the three ticks read as different colours */
.h27 .h27-ail__facts li::before {
  content: ''; position: absolute; left: 0; top: 22px;
  width: 10px; height: 2px; border-radius: 1px; background: var(--h27-coral);
}
.h27 .h27-ail__facts b {
  display: block; margin-bottom: 4px;
  font-size: 14.5px; font-weight: 800; letter-spacing: -.005em; color: #fff;
}
.h27 .h27-ail__facts span { display: block; font-size: 14px; line-height: 1.6; color: var(--h27-onink-mute); }
.h27 .h27-ail__cta { margin-top: clamp(22px, 2.6vw, 30px); }

/* ── the console ──────────────────────────────────────────────────────── */
/* Runs a four-beat cycle driven by home-2027.js §9, with the phase held on
   #askConsole as [data-phase]: ask -> think -> answer -> rest. Every state is
   also reachable from markup alone, so the server-rendered "answered" frame is
   what shows with JS off or motion reduced — the script only animates between
   frames it could already paint. */
.h27 .h27-ail__stage { position: relative; }
.h27 .h27-ail__glow {
  position: absolute; inset: -16% -12% -20%; z-index: 0; pointer-events: none;
  filter: blur(28px);
  background:
    none;
  opacity: .84;
  transition: opacity 900ms var(--h27-ease), transform 900ms var(--h27-ease);
}
/* the glow leans in while the model is working — the panel looks powered.
   :has() only; the glow sits before the window in source order, so no sibling
   combinator reaches it. Browsers without :has() just keep the resting glow. */
.h27 .h27-ail__stage:has(.h27-ail__win[data-phase="think"]) .h27-ail__glow {
  opacity: 1; transform: scale(1.05);
}
.h27 .h27-ail__stage:has(.h27-ail__win[data-phase="rest"]) .h27-ail__glow { opacity: .7; }

.h27 .h27-ail__win {
  position: relative; z-index: 1; overflow: hidden;
  padding: clamp(16px, 1.9vw, 24px);
  border-radius: 10px;
  background: rgba(20,36,79,.94);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12), 0 30px 70px rgba(0,0,0,.5);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  transition: box-shadow 700ms var(--h27-ease);
}
.h27 .h27-ail__win[data-phase="think"] {
  box-shadow: inset 0 0 0 1px rgba(255,171,150,.26), 0 30px 70px rgba(0,0,0,.5);
}

/* faint navigation grid behind the panel — drifts, never reads as a pattern */
.h27 .h27-ail__mesh {
  position: absolute; inset: 0; z-index: 0; pointer-events: none; opacity: .5;
  background-image:
    linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px);
  background-size: 34px 34px;
  -webkit-mask-image: radial-gradient(72% 62% at 74% 16%, #000 0%, transparent 78%);
          mask-image: radial-gradient(72% 62% at 74% 16%, #000 0%, transparent 78%);
  animation: h27-ail-mesh 26s linear infinite;
}
@keyframes h27-ail-mesh { to { background-position: 34px 34px, 34px 34px; } }

/* Round-6 corner brackets, in the panel's own idiom */
.h27 .h27-ail__corner {
  position: absolute; z-index: 2; width: 16px; height: 16px; pointer-events: none;
  border: 0 solid var(--h27-coral); opacity: .55;
  transition: opacity 600ms var(--h27-ease);
}
.h27 .h27-ail__corner--tl { top: 9px; left: 9px; border-top-width: 2px; border-left-width: 2px; }
.h27 .h27-ail__corner--br { right: 9px; bottom: 9px; border-bottom-width: 2px; border-right-width: 2px; }
.h27 .h27-ail__win[data-phase="think"] .h27-ail__corner { opacity: 1; }

.h27 .h27-ail__winbar {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 11px;
  padding-bottom: 15px;
  border-bottom: 1px solid rgba(255,255,255,.09);
}
.h27 .h27-ail__spark {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(232,89,58,.18);
  box-shadow: inset 0 0 0 1px rgba(255,171,150,.34);
  transition: transform 500ms var(--h27-ease), box-shadow 500ms var(--h27-ease);
}
.h27 .h27-ail__spark .ico { width: 15px; height: 15px; font-size: 15px; color: var(--h27-coral-lt); }
.h27 .h27-ail__win[data-phase="think"] .h27-ail__spark {
  transform: scale(1.08);
  box-shadow: inset 0 0 0 1px rgba(255,171,150,.6), 0 0 18px rgba(232,89,58,.4);
}
.h27 .h27-ail__win[data-phase="think"] .h27-ail__spark .ico { animation: h27-ail-spark 1.5s var(--h27-ease) infinite; }
@keyframes h27-ail-spark {
  0%, 100% { transform: rotate(0) scale(1); opacity: 1; }
  50%      { transform: rotate(90deg) scale(.86); opacity: .7; }
}

.h27 .h27-ail__wintitle {
  font-family: 'Inter', sans-serif;
  font-size: 11px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: #fff;
}
/* status chip — one element, four states, colour carried by --st */
.h27 .h27-ail__live {
  --st: 122,158,142;
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 8px;
  font-family: 'Inter', sans-serif;
  font-size: 10.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--h27-onink-mute);
  transition: color 400ms var(--h27-ease);
}
.h27 .h27-ail__live[data-state="think"] { --st: 232,89,58; color: var(--h27-coral-lt); }
.h27 .h27-ail__live[data-state="done"]  { --st: 122,158,142; color: var(--h27-sage-lt); }
.h27 .h27-ail__live[data-state="ask"]   { --st: 59,134,188; }
/* the beat where it has stopped and is holding for a decision */
.h27 .h27-ail__live[data-state="wait"]  { --st: 232,89,58; color: var(--h27-coral-lt); }
.h27 .h27-ail__live em { font-style: normal; }
.h27 .h27-ail__live i {
  width: 6px; height: 6px; border-radius: 50%; background: rgb(var(--st));
  animation: h27-ail-pulse 2.4s var(--h27-ease) infinite;
  transition: background 400ms var(--h27-ease);
}
.h27 .h27-ail__live[data-state="think"] i { animation-duration: 1s; }
@keyframes h27-ail-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(var(--st), .55); }
  70%  { box-shadow: 0 0 0 8px rgba(var(--st), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--st), 0); }
}

/* ── the thread — the conversation, newest turn at the bottom ─────────────
   A fixed-height scroller rather than a growing panel: the follow-up turn
   pushes the one it answers up out of view, which is what makes the console
   read as a conversation that is still running instead of a static answer. */
.h27 .h27-ail__thread {
  position: relative; z-index: 1;
  margin: 14px -2px 0; padding: 2px 2px 0;
  max-height: clamp(320px, 32vw, 468px);
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: none; -ms-overflow-style: none;
  display: flex; flex-direction: column; gap: 17px;
  /* the top edge dissolves, so a turn scrolling out does not shear off */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 20px);
          mask-image: linear-gradient(180deg, transparent 0, #000 20px);
}
/* Once the script owns it the box is a fixed frame rather than a growing one —
   the panel must not resize under the reader between turns — and the thread
   hangs from the bottom of it, so a turn part-way through answering sits where
   the finished one will, not stranded at the top of an empty box. The auto
   margin resolves to zero as soon as the content outgrows the frame, so this
   costs nothing once it is scrolling. */
.h27 .h27-ail__win.is-live .h27-ail__thread { height: clamp(320px, 32vw, 468px); }
.h27 .h27-ail__win.is-live .h27-ail__turn:first-child { margin-top: auto; }
.h27 .h27-ail__thread::-webkit-scrollbar { width: 0; height: 0; }
/* A finished conversation dims but stays up while the next question is being
   typed — clearing it first left the panel empty for two full seconds, which
   is long enough to be the frame a reader arrives on. It is wiped at the
   moment the new question is sent, so the swap always happens under a
   movement rather than in dead air. */
.h27 .h27-ail__thread.is-clear { opacity: .25; transition: opacity 460ms var(--h27-ease); }

.h27 .h27-ail__turn { display: grid; gap: 12px; }
.h27 .h27-ail__turn.is-new { animation: h27-ail-turn 520ms var(--h27-ease) both; }
@keyframes h27-ail-turn {
  from { opacity: 0; transform: translate3d(0, 16px, 0); }
  to   { opacity: 1; transform: none; }
}
/* Once it has been answered and asked about, a turn keeps only what was said
   and what came back — the working is history at that point, and leaving it
   expanded would push the live turn off the bottom of the scroller. */
.h27 .h27-ail__turn.is-past { opacity: .55; }
/* kept: the question, the places it reached into, and what it said back —
   that is the part the follow-up is a follow-up to. Dropped: the plan and the
   itemised result, which are working, and the approval bar, which would go on
   claiming to be waiting on a decision you have visibly already made. The
   .is-in and .is-folded states are more specific than a bare .is-past, so
   those have to be named here rather than relying on source order. */
.h27 .h27-ail__turn.is-past .h27-ail__plan,
.h27 .h27-ail__turn.is-past .h27-ail__out,
.h27 .h27-ail__win.is-live .h27-ail__turn.is-past .h27-ail__out li,
.h27 .h27-ail__win.is-live .h27-ail__turn.is-past .h27-ail__out li.is-in,
.h27 .h27-ail__win.is-live .h27-ail__turn.is-past .h27-ail__act,
.h27 .h27-ail__win.is-live .h27-ail__turn.is-past .h27-ail__act.is-in,
.h27 .h27-ail__win.is-live .h27-ail__turn.is-past .h27-ail__foot,
.h27 .h27-ail__win.is-live .h27-ail__turn.is-past .h27-ail__foot.is-in { display: none; }

/* what you asked — right-hung, persimmon-tinted, the one warm block in the run */
.h27 .h27-ail__you { display: flex; justify-content: flex-end; }
.h27 .h27-ail__you p {
  max-width: 86%; margin: 0;
  padding: 9px 14px; border-radius: 13px 13px 3px 13px;
  background: rgba(232,89,58,.17);
  box-shadow: inset 0 0 0 1px rgba(255,171,150,.3);
  font-family: 'Inter', sans-serif;
  font-size: 13.5px; font-weight: 600; line-height: 1.5; color: #fff;
}

/* what came back — marked, indented under the mark, everything in one column */
.h27 .h27-ail__bot { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 11px; }
.h27 .h27-ail__mark {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(59,134,188,.2);
  box-shadow: inset 0 0 0 1px rgba(139,190,221,.34);
}
.h27 .h27-ail__mark .ico { width: 13px; height: 13px; font-size: 13px; color: var(--h27-sky-lt); }
.h27 .h27-ail__win[data-phase="think"] .h27-ail__turn:last-child .h27-ail__mark {
  box-shadow: inset 0 0 0 1px rgba(255,171,150,.55), 0 0 16px rgba(232,89,58,.32);
}
.h27 .h27-ail__body { min-width: 0; display: grid; gap: 11px; align-content: start; }

/* how it is going about it — stated before it starts, so the work has a reason */
.h27 .h27-ail__plan {
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  font-family: 'Inter', sans-serif; font-size: 12.5px; line-height: 1.55;
  color: var(--h27-onink-mute);
}
.h27 .h27-ail__plan b {
  flex: 0 0 auto;
  font-size: 10px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  color: var(--h27-coral-lt);
}
.h27 .h27-ail__plan span { flex: 1 1 12ch; min-width: 0; }

/* ── the ask bar — the prompt is typed in, character by character ──────── */
.h27 .h27-ail__ask {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 12px;
  margin-top: 15px; padding: 12px 12px 12px 16px;
  border-radius: 8px;
  background: rgba(255,255,255,.05);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.14);
  transition: box-shadow 420ms var(--h27-ease), background 420ms var(--h27-ease);
}
.h27 .h27-ail__win[data-phase="ask"] .h27-ail__ask {
  background: rgba(255,255,255,.07);
  box-shadow: inset 0 0 0 1px rgba(255,171,150,.42), 0 0 0 3px rgba(232,89,58,.09);
}
/* the ring the bar throws when the prompt is sent */
.h27 .h27-ail__ask.is-sent { animation: h27-ail-sent 620ms var(--h27-ease); }
@keyframes h27-ail-sent {
  0%   { box-shadow: inset 0 0 0 1px rgba(255,171,150,.7), 0 0 0 0 rgba(232,89,58,.34); }
  100% { box-shadow: inset 0 0 0 1px rgba(255,255,255,.14), 0 0 0 16px rgba(232,89,58,0); }
}
.h27 .h27-ail__q {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center;
  font-family: 'Inter', sans-serif;
  font-size: clamp(13.5px, 1.05vw, 15.5px);
  min-height: 1.65em;
}
.h27 .h27-ail__typed {
  min-width: 0; font-size: inherit; font-weight: 600; line-height: 1.65em; color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* the composer holds a placeholder between turns, the way a real one would —
   an empty bar under a running answer reads as a dead control */
.h27 .h27-ail__hint {
  display: none;
  font-size: inherit; font-weight: 500; line-height: 1.65em;
  color: rgba(255,255,255,.38);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.h27 .h27-ail__ask.is-empty .h27-ail__hint { display: inline; }
.h27 .h27-ail__caret {
  flex: 0 0 auto; display: inline-block;
  width: 2px; height: 1em; margin-left: 7px;
  background: var(--h27-coral);
  animation: h27-ail-caret 1.05s steps(1) infinite;
}
@keyframes h27-ail-caret { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }
/* while characters are landing the caret holds solid — a blink mid-word reads
   as a second cursor */
.h27 .h27-ail__win[data-phase="ask"] .h27-ail__caret { animation: none; opacity: 1; }
.h27 .h27-ail__win[data-phase="think"] .h27-ail__caret,
.h27 .h27-ail__win[data-phase="answer"] .h27-ail__caret { opacity: 0; animation: none; }

.h27 .h27-ail__send {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 7px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 15px; color: #fff;
  background: var(--h27-coral);
  box-shadow: 0 8px 20px rgba(232,89,58,.3);
  transition: transform 380ms var(--h27-ease), background 380ms var(--h27-ease);
}
.h27 .h27-ail__send.is-fired { animation: h27-ail-fire 540ms var(--h27-ease); }
@keyframes h27-ail-fire {
  0%   { transform: scale(1) translateX(0); }
  32%  { transform: scale(.9) translateX(-2px); }
  62%  { transform: scale(1.06) translateX(3px); }
  100% { transform: scale(1) translateX(0); }
}
/* dim to a "sent, nothing to do" state until the next prompt starts */
.h27 .h27-ail__win[data-phase="think"] .h27-ail__send,
.h27 .h27-ail__win[data-phase="answer"] .h27-ail__send { background: var(--h27-coral-dk); }

/* the scan line only runs while the model is actually working. It sits
   directly under the window bar rather than under the composer — at the top
   of the panel it reads as the whole console being busy, which is what is
   actually true while a turn is running. */
.h27 .h27-ail__scan {
  position: relative; z-index: 1; overflow: hidden;
  height: 2px; margin: 13px 2px 0; border-radius: 2px;
  background: rgba(255,255,255,.08);
  opacity: 0; transition: opacity 400ms var(--h27-ease);
}
.h27 .h27-ail__win[data-phase="think"] .h27-ail__scan { opacity: 1; }
.h27 .h27-ail__scan i {
  position: absolute; top: 0; bottom: 0; left: -38%; width: 38%; border-radius: 2px;
  background: var(--h27-coral-lt);
  animation: h27-ail-scan 1.5s var(--h27-ease) infinite;
  animation-play-state: paused;
}
.h27 .h27-ail__win[data-phase="think"] .h27-ail__scan i { animation-play-state: running; }
@keyframes h27-ail-scan { 0% { left: -38%; } 100% { left: 100%; } }

/* ── the work trace — what it did, ticked off as it goes ───────────────── */
.h27 .h27-ail__trace {
  position: relative; z-index: 1;
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 7px;
}
.h27 .h27-ail__trace li {
  display: flex; align-items: center; gap: 10px;
  font-family: 'Inter', sans-serif; font-size: 12.5px; line-height: 1.4;
  color: rgba(255,255,255,.34);
  opacity: 0; transform: translate3d(-6px, 0, 0);
  transition: opacity 420ms var(--h27-ease), transform 420ms var(--h27-ease), color 420ms var(--h27-ease);
}
.h27 .h27-ail__step { flex: 1 1 auto; min-width: 0; }
/* Once it starts writing, the working folds down to the ticks and the places
   it reached into — the same move a person makes when they stop narrating and
   hand over the answer. It also buys back the ~45px that keeps the question
   itself on screen next to the reply it produced. */
.h27 .h27-ail__turn.is-folded .h27-ail__trace {
  display: flex; flex-wrap: wrap; gap: 6px 9px;
}
.h27 .h27-ail__turn.is-folded .h27-ail__trace li { gap: 6px; }
.h27 .h27-ail__turn.is-folded .h27-ail__step { display: none; }
/* folded, the ring-and-tick marker is a 13px scribble next to a 9px chip —
   it drops to the sage dot that says the same thing at that size */
.h27 .h27-ail__turn.is-folded .h27-ail__spin {
  width: 5px; height: 5px; background: var(--h27-sage); box-shadow: none;
}
.h27 .h27-ail__turn.is-folded .h27-ail__spin::before,
.h27 .h27-ail__turn.is-folded .h27-ail__spin::after { display: none; }
/* which part of the practice that step reached into — the claim on the left
   says "it works inside your data", and this is that claim, itemised */
.h27 .h27-ail__tool {
  flex: 0 0 auto; font-style: normal;
  padding: 2px 7px; border-radius: 999px;
  font-size: 9.5px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: rgba(139,190,221,.92);
  background: rgba(59,134,188,.16);
  box-shadow: inset 0 0 0 1px rgba(139,190,221,.24);
}
.h27 .h27-ail__trace li.is-run .h27-ail__tool {
  color: #fff; background: rgba(232,89,58,.2); box-shadow: inset 0 0 0 1px rgba(255,171,150,.4);
}
.h27 .h27-ail__trace li.is-run,
.h27 .h27-ail__trace li.is-done { opacity: 1; transform: none; }
.h27 .h27-ail__trace li.is-run { color: #fff; }
.h27 .h27-ail__trace li.is-done { color: var(--h27-onink-mute); }
/* the marker is a ring that spins while the step runs, then fills to a tick */
.h27 .h27-ail__spin {
  position: relative; flex: 0 0 auto; width: 13px; height: 13px; border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.18);
  transition: box-shadow 380ms var(--h27-ease);
}
.h27 .h27-ail__spin::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px solid transparent; border-top-color: var(--h27-coral-lt);
  opacity: 0; transition: opacity 260ms linear;
  animation: h27-ail-spin .72s linear infinite;
}
.h27 .h27-ail__trace li.is-run .h27-ail__spin::before { opacity: 1; }
@keyframes h27-ail-spin { to { transform: rotate(360deg); } }
/* the tick: two strokes drawn from the centre out, so it "writes" itself */
.h27 .h27-ail__spin::after {
  content: ''; position: absolute; left: 4px; top: 6px;
  width: 4px; height: 2px; border-radius: 1px;
  background: var(--h27-sage);
  transform: scale(0) rotate(45deg); transform-origin: left center;
  box-shadow: 3px -3px 0 -.5px var(--h27-sage), 5px -5px 0 -.5px var(--h27-sage);
  transition: transform 320ms var(--h27-ease);
}
.h27 .h27-ail__trace li.is-done .h27-ail__spin { box-shadow: inset 0 0 0 1.5px rgba(122,158,142,.5); }
.h27 .h27-ail__trace li.is-done .h27-ail__spin::after { transform: scale(1) rotate(45deg); }

/* ── what it says back — arrives a word at a time, the way it is written ──
   Word spans, not a character typewriter: at reading size a per-character
   crawl on a full sentence is slow enough to be irritating, and text landing
   in whole words is what a model actually does. */
.h27 .h27-ail__reply {
  position: relative; z-index: 1; margin: 0;
  font-family: 'Inter', sans-serif; font-size: 13.5px; line-height: 1.62; color: #fff;
}
.h27 .h27-ail__reply span { transition: opacity 240ms linear; }
/* the paragraph itself takes no room until it starts arriving; the words
   inside it do keep their space, so the lines do not reflow under the reader */
.h27 .h27-ail__win.is-live .h27-ail__reply { display: none; }
.h27 .h27-ail__win.is-live .h27-ail__reply.is-on { display: block; }
.h27 .h27-ail__win.is-live .h27-ail__reply span { opacity: 0; }
.h27 .h27-ail__win.is-live .h27-ail__reply span.is-on { opacity: 1; }
/* the block cursor that trails the words while they are still landing */
.h27 .h27-ail__reply.is-writing::after {
  content: ''; display: inline-block; vertical-align: -1px;
  width: 7px; height: 13px; margin-left: 4px; border-radius: 1px;
  background: var(--h27-coral);
  animation: h27-ail-caret 1.05s steps(1) infinite;
}

/* ── what came back — rows land one at a time ─────────────────────────── */
.h27 .h27-ail__out {
  position: relative; z-index: 1;
  list-style: none; margin: 0; padding: 0; display: grid; gap: 9px;
}
.h27 .h27-ail__out li {
  display: grid; grid-template-columns: 26px minmax(0, 1fr) auto;
  align-items: center; column-gap: 12px; row-gap: 2px;
  padding: 10px 13px; border-radius: 8px;
  background: rgba(255,255,255,.045);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.09);
  transition: opacity 620ms var(--h27-ease), transform 620ms var(--h27-ease),
              background 300ms var(--h27-ease), box-shadow 300ms var(--h27-ease);
}
.h27 .h27-ail__out li:hover { background: rgba(255,255,255,.075); }
/* the row that just landed keeps a coral edge for a beat */
.h27 .h27-ail__out li.is-fresh { box-shadow: inset 0 0 0 1px rgba(255,171,150,.4), inset 3px 0 0 0 var(--h27-coral); }
/* Hidden states are gated twice: html.h27-ready (the script booted at all) and
   .is-live (this console specifically took over). If either is missing, the
   server-rendered answered frame stays fully visible. */
html.h27-ready .h27 .h27-ail__out li { opacity: 0; transform: translate3d(0, 12px, 0); }
html.h27-ready .h27 .h27-ail__stage.x27-in .h27-ail__out li,
.h27 .h27-ail__stage.x27-in .h27-ail__out li { opacity: 1; transform: none; }
.h27 .h27-ail__out li:nth-child(1) { transition-delay: .40s; }
.h27 .h27-ail__out li:nth-child(2) { transition-delay: .56s; }
.h27 .h27-ail__out li:nth-child(3) { transition-delay: .72s; }
/* Once the script drives the conversation the rows are not hidden, they are
   absent: a turn that has not answered yet must not reserve the height of the
   answer it is about to give, or the thread cannot grow from the bottom and
   the question scrolls off the top of a box full of nothing. display:none is
   why the arrival has to be an animation rather than a transition. */
.h27 .h27-ail__win.is-live .h27-ail__out li { display: none; }
.h27 .h27-ail__win.is-live .h27-ail__out li.is-in {
  display: grid; opacity: 1; transform: none;
  animation: h27-ail-land 560ms var(--h27-ease) both;
}
@keyframes h27-ail-land {
  from { opacity: 0; transform: translate3d(0, 14px, 0) scale(.985); }
  to   { opacity: 1; transform: none; }
}

.h27 .h27-ail__tick {
  grid-row: span 2;
  width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(122,158,142,.20);
  box-shadow: inset 0 0 0 1px rgba(122,158,142,.45);
}
.h27 .h27-ail__tick .ico { width: 13px; height: 13px; font-size: 13px; color: var(--h27-sage-lt); }
.h27 .h27-ail__win.is-live .h27-ail__out li.is-in .h27-ail__tick { animation: h27-ail-ring 900ms var(--h27-ease); }
@keyframes h27-ail-ring {
  0%   { box-shadow: inset 0 0 0 1px rgba(122,158,142,.45), 0 0 0 0 rgba(122,158,142,.42); transform: scale(.82); }
  55%  { transform: scale(1.04); }
  100% { box-shadow: inset 0 0 0 1px rgba(122,158,142,.45), 0 0 0 12px rgba(122,158,142,0); transform: scale(1); }
}
.h27 .h27-ail__out b { font-family: 'Inter', sans-serif; font-size: 13.5px; font-weight: 700; color: #fff; }
.h27 .h27-ail__out > li > span:not(.h27-ail__tick) {
  grid-column: 2 / -1;
  font-family: 'Inter', sans-serif; font-size: 12.5px; line-height: 1.45; color: var(--h27-onink-mute);
}
/* The chip on the right of each row — it carries the row's state, not
   decoration. Every chip used to be persimmon, which put three identical
   alarms down the right rail of a panel whose whole point is that most of the
   work came back clean: a plain count read exactly as loud as "high risk",
   and the accent budget was gone before the send button and the approval bar
   (the two controls that actually earn it). So the chip now has three tones —
   a quiet default for a count, sage for a row that finished, persimmon kept
   for the one row that needs a person — and the tone is data, set from the
   turn in home-2027.js §9 rather than guessed from the wording. */
.h27 .h27-ail__meta {
  grid-column: 3; grid-row: 1;
  justify-self: end;
  padding: 3px 8px; border-radius: 999px;
  font-family: 'Inter', sans-serif; font-style: normal;
  font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  white-space: nowrap;
  color: var(--h27-onink-mute);
  background: rgba(255,255,255,.06);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12);
}
/* it is done — same sage the tick beside it already uses */
.h27 .h27-ail__meta[data-tone="good"] {
  color: var(--h27-sage-lt);
  background: rgba(122,158,142,.16);
  box-shadow: inset 0 0 0 1px rgba(169,207,190,.3);
}
/* it stopped and is waiting on you — one per turn, by design */
.h27 .h27-ail__meta[data-tone="flag"] {
  color: var(--h27-coral-lt);
  background: rgba(232,89,58,.14);
  box-shadow: inset 0 0 0 1px rgba(255,171,150,.28);
}

/* ── what it is waiting on you for ────────────────────────────────────────
   The turn ends on a decision, not on a result. This is the whole promise of
   the section — it does the work and then stops at your desk — so it is the
   last thing in the answer, and the status chip says "waiting on you" for as
   long as it is on screen. */
.h27 .h27-ail__act {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  transition: opacity 460ms var(--h27-ease), transform 460ms var(--h27-ease);
}
.h27 .h27-ail__win.is-live .h27-ail__act { display: none; }
.h27 .h27-ail__win.is-live .h27-ail__act.is-in {
  display: flex; animation: h27-ail-land 520ms var(--h27-ease) both;
}
.h27 .h27-ail__pill {
  padding: 7px 13px; border-radius: 999px;
  font-family: 'Inter', sans-serif; font-size: 12px; font-weight: 700; color: #fff;
  background: rgba(255,255,255,.06);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.16);
  transition: background 300ms var(--h27-ease), box-shadow 300ms var(--h27-ease),
              transform 300ms var(--h27-ease);
}
/* tinted, not a solid persimmon fill — the send button is already the one
   solid persimmon control in the panel and two of them fight */
.h27 .h27-ail__pill--go {
  background: rgba(232,89,58,.22);
  box-shadow: inset 0 0 0 1px rgba(255,171,150,.5);
}
.h27 .h27-ail__pill:hover { transform: translate3d(0, -1px, 0); background: rgba(255,255,255,.1); }
.h27 .h27-ail__pill--go:hover { background: rgba(232,89,58,.34); }
.h27 .h27-ail__wait {
  margin-left: auto;
  font-family: 'Inter', sans-serif;
  font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--h27-coral-lt); white-space: nowrap;
}

/* ── the turn's tally — what the whole exchange was worth, in one line ──── */
.h27 .h27-ail__foot {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 9px;
  margin-top: 2px; padding-top: 12px;
  border-top: 1px solid rgba(255,255,255,.09);
  font-family: 'Inter', sans-serif; font-size: 12.5px; color: var(--h27-onink-mute);
  transition: opacity 400ms var(--h27-ease);
}
.h27 .h27-ail__fdot {
  flex: 0 0 auto; width: 5px; height: 5px; border-radius: 50%; background: var(--h27-coral);
}
.h27 .h27-ail__win.is-live .h27-ail__foot { display: none; }
.h27 .h27-ail__win.is-live .h27-ail__foot.is-in {
  display: flex; animation: h27-ail-fade 460ms var(--h27-ease) both;
}
@keyframes h27-ail-fade { from { opacity: 0; } to { opacity: 1; } }

/* the console's rolling prompts are decorative; this carries them to AT */
.h27 .h27-vh {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ══ §9 · ACT 03 · PRODUCT STAGE ═════════════════════════════════════════ */

.h27 .h27-stage {
  position: relative;
  margin-top: clamp(28px, 4vw, 56px);
  perspective: 1800px;
}
.h27 .h27-stage__glow {
  position: absolute; inset: 6% -4% -12%;
  z-index: 0; pointer-events: none;
  background: none;
  filter: blur(30px);
}
.h27 .h27-stage > .home-dash-wrap,
.h27 .h27-stage > .dashboard-v2 { position: relative; z-index: 1; }

/* ── §9b · THE CANVAS INSIDE THE MOCK ────────────────────────────────────
   The app mock is the one place on the page that has to look like software
   rather than like a website, and the KPI row was the tell: four pastel
   blocks ~110px tall, each a stack of icon chip / label / value / chunky
   sparkline. Rebuilt as a flat analytics strip — white surface like every
   other card in the mock, colour carried by the icon and the viz instead of
   a full-bleed tint, and two lines instead of four. That returns ~45px per
   card to the 560px body, which is what lets the chart get a real y-axis and
   the schedule show its fifth appointment.

   Scoped under .h27 so styles.css's global .dashboard-v2 rules stay intact
   for any other surface that adopts the mock later. ──────────────────── */

.h27 .dashboard-v2__metrics { gap: 10px; }

.h27 .dashboard-v2__metric {
  background: var(--h27-paper);
  border: 1px solid var(--h27-hair);
  border-radius: 12px;
  padding: 10px 12px 11px;
  gap: 7px;
  box-shadow: 0 1px 2px rgba(13,27,62,.04);
}
/* the left accent bar was the fourth colour event on a card that only needed
   one — the icon now carries the module identity */
.h27 .dashboard-v2__metric::before { display: none; }
.h27 .dashboard-v2__metric:hover {
  border-color: rgba(59,134,188,.28);
  box-shadow: 0 4px 14px rgba(13,27,62,.07);
  transform: translateY(-2px);
}

.h27 .dashboard-v2__metric-top {
  display: flex; align-items: center; gap: 6px; min-width: 0;
}

.h27 .dashboard-v2__metric-icon {
  width: 16px; height: 16px; border-radius: 5px;
  margin-bottom: 0; flex: 0 0 auto;
}
.h27 .dashboard-v2__metric-icon svg { width: 9px; height: 9px; }

.h27 .dashboard-v2__metric-label {
  font-size: 8.5px; font-weight: 700; letter-spacing: .08em;
  color: var(--h27-mute);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.h27 .dashboard-v2__metric-row {
  display: flex; align-items: center; gap: 7px; min-width: 0;
}

.h27 .dashboard-v2__metric-val {
  font-size: 19px; letter-spacing: -.045em;
  font-variant-numeric: tabular-nums; flex: 0 0 auto;
}

/* a bare "↑ 18%" is a claim; the basis after it is what makes it read as a
   number a real system computed */
.h27 .dashboard-v2__delta {
  font-size: 9px; font-weight: 700; padding: 2px 6px; border-radius: 5px;
  display: inline-flex; align-items: baseline; gap: 3px; flex: 0 0 auto;
  background: rgba(13,27,62,.05); color: var(--h27-mute);
}
.h27 .dashboard-v2__delta em {
  font-style: normal; font-weight: 600; font-size: 8px; opacity: .72;
}
.h27 .dashboard-v2__delta--up { background: rgba(122,158,142,.14); color: #4F7263; }

/* Inline sparkline — sits on the value baseline instead of below the card,
   and the newest bar is solid so the eye lands on "now". */
.h27 .dashboard-v2__metric-spark {
  display: flex; align-items: flex-end; gap: 2px;
  height: 20px; margin-top: 0; margin-left: auto;
  flex: 1 1 auto; min-width: 34px; max-width: 84px;
}
.h27 .dashboard-v2__metric-spark span { border-radius: 1.5px; min-height: 2px; }

.h27 .dashboard-v2__metric--blue  .dashboard-v2__metric-spark span { background: rgba(59,134,188,.22); }
.h27 .dashboard-v2__metric--green .dashboard-v2__metric-spark span { background: rgba(122,158,142,.24); }
.h27 .dashboard-v2__metric--teal  .dashboard-v2__metric-spark span { background: rgba(232,89,58,.20); }
.h27 .dashboard-v2__metric--blue  .dashboard-v2__metric-spark span:last-child { background: #3B86BC; }
.h27 .dashboard-v2__metric--green .dashboard-v2__metric-spark span:last-child { background: #7A9E8E; }
.h27 .dashboard-v2__metric--teal  .dashboard-v2__metric-spark span:last-child { background: #E8593A; }

.h27 .dashboard-v2__metric--teal .dashboard-v2__metric-icon {
  background: rgba(232,89,58,.10); color: var(--h27-coral-dk);
}

/* Meter replaces the donut on the claims tile — same information, a third of
   the height, and it lines up with the sparklines beside it. Segmented rather
   than solid: at 97% a continuous bar just reads as a rule, the notches make
   it read as a gauge. */
.h27 .dashboard-v2__metric-meter {
  position: relative; display: block; height: 13px;
  margin-left: auto; flex: 1 1 auto; min-width: 34px; max-width: 84px;
  overflow: hidden;
  --meter-mask: repeating-linear-gradient(90deg,
                  #000 0 3px, transparent 3px 6px);
  background: rgba(13,27,62,.14);
  -webkit-mask-image: var(--meter-mask); mask-image: var(--meter-mask);
}
.h27 .dashboard-v2__metric-meter i {
  position: absolute; inset: 0 auto 0 0;
  background: #2C6FA6;
}

/* ── Revenue chart ──────────────────────────────────────────────────────
   Was a 46-unit-tall strip with no axis, distorted end dots and a callout
   that overhung the top edge (the card clips, so it lost its cap). Now: a
   labelled y-axis, three gridlines, and the point markers lifted out of the
   stretched SVG into HTML so they stay circular at any card width. */

.h27 .dashboard-v2__chart { gap: 6px; }

.h27 .dashboard-v2__plot {
  flex: 1; display: flex; gap: 8px; min-height: 0;
}

/* the labels are pinned to the same percentages as the gridlines rather than
   spaced with padding, so they stay on the lines at any card height */
.h27 .dashboard-v2__plot-y {
  position: relative;
  width: 26px; flex: 0 0 auto;
  font-size: 7.5px; font-weight: 600; letter-spacing: .02em;
  color: var(--h27-mute);
}
.h27 .dashboard-v2__plot-y span {
  position: absolute; right: 0; transform: translateY(-50%);
  white-space: nowrap;
}
.h27 .dashboard-v2__plot-y span:nth-child(1) { top: 10%; }
.h27 .dashboard-v2__plot-y span:nth-child(2) { top: 40%; }
.h27 .dashboard-v2__plot-y span:nth-child(3) { top: 70%; }

.h27 .dashboard-v2__plot-area { position: relative; flex: 1; min-width: 0; }

.h27 .dashboard-v2__plot-area svg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  min-height: 0; max-height: none; flex: none;
}

.h27 .dashboard-v2__plot-node,
.h27 .dashboard-v2__plot-dot {
  position: absolute; border-radius: 50%;
  transform: translate(-50%, -50%); pointer-events: none;
}
.h27 .dashboard-v2__plot-node {
  width: 4px; height: 4px;
  background: #fff; box-shadow: 0 0 0 1.5px rgba(59,134,188,.55);
}
.h27 .dashboard-v2__plot-dot {
  width: 7px; height: 7px;
  background: #fff; box-shadow: 0 0 0 2px #2C6FA6, 0 0 0 7px rgba(59,134,188,.12);
}

/* the flag hangs to the LEFT of the last point so it stays inside the card
   instead of being cropped by its overflow */
.h27 .dashboard-v2__plot-flag {
  position: absolute; transform: translate(calc(-100% - 11px), -50%);
  background: #163760; color: #fff;
  font-size: 8.5px; font-weight: 700; letter-spacing: -.01em;
  padding: 2px 6px; border-radius: 5px; white-space: nowrap;
  box-shadow: 0 2px 6px rgba(13,27,62,.18);
}

.h27 .dashboard-v2__chart-months {
  padding-left: 34px;  /* y-axis column + its gap, so Oct/Mar sit under the data */
  font-size: 8px;
}

/* ── The canvas: white, not bone ────────────────────────────────────────
   styles.css paints the app area #F8F6F1 — a warm bone. Sat on the cool
   ice section behind it, that warmth reads as dust on the screenshot
   rather than as a product surface. The mock is white throughout now;
   separation comes from hairlines and the tiles' own borders, not a tint. */

.h27 .dashboard-v2,
.h27 .dashboard-v2__main { background: #FFFFFF; }

/* browser chrome goes white too — the traffic lights and the pills carry
   it, so the pills take the fill the bar used to have */
.h27 .dashboard-v2__chrome {
  background: #FFFFFF;
  border-bottom: 1px solid var(--h27-hair);
}
.h27 .dashboard-v2__urlbar,
.h27 .dashboard-v2__chrome-search {
  background: var(--h27-ice);
  border-color: var(--h27-hair);
  backdrop-filter: none; -webkit-backdrop-filter: none;
}

/* cards were white-on-bone; white-on-white needs the hairline to do the
   work the tint used to */
.h27 .dashboard-v2__card,
.h27 .dashboard-v2__quick-actions {
  background: #FFFFFF;
  border: 1px solid var(--h27-hair);
  box-shadow: 0 1px 2px rgba(13,27,62,.04);
}

/* the one panel that keeps a fill — it's the AI band, and it's cool */
.h27 .dashboard-v2__ai-card {
  background: rgba(59,134,188,.04);
  border: 1px solid var(--h27-hair);
}

/* the action chips were bone; ice keeps them legible against white */
.h27 .dashboard-v2__qa-btn {
  background: var(--h27-ice);
  border: 1px solid var(--h27-hair);
}
.h27 .dashboard-v2__qa-btn:hover {
  background: rgba(59,134,188,.06);
  border-color: rgba(59,134,188,.24);
}

/* ── THE CLIP FIX — load-bearing, not cosmetic ──────────────────────────
   §31 reveals every [data-h27] into `clip-path: inset(0 0 0 0)`, which clips
   the element to its own border box. Only the "mask" variant needs that (it
   animates from inset(0 0 100% 0)); "rise" is opacity + transform and never
   asked for it. The dashboard stage reveals with "rise", so the clip bought
   the animation nothing and cost it everything that paints outside the frame:
   the stage glow was cut to a hard rectangle, and the proof badges — which
   hang off the corners on negative offsets — were sliced clean in half. That
   half-a-pill above the chrome bar is the artefact, not a z-index problem.
   module-2027.css §? carries the identical fix for the t27 layer.

   The revealed rule is `html.h27-ready .h27 [data-h27].x27-in` at (0,4,1), so
   a plain class pair would lose. The id anchor clears it outright; the second
   selector covers the pre-gate and no-JS paths. */
html.h27-ready .h27 #home-dashboard .h27-stage.x27-in,
.h27 #home-dashboard .h27-stage { clip-path: none; }

/* ── Floating proof badges ──────────────────────────────────────────────
   Round-7 (2026-08-03). The corner placement (top:-20px / bottom:-26px) still
   left ~45% of each right-hand badge lying on live UI: "$42K Revenue" covered
   the chrome's search field and the DR avatar, "AI Scribe Active" covered the
   Send Campaign quick action. Two things made it worse than the numbers look:

   · animations.css still runs `floatBadge2026` on .float-badge. A keyframe
     animation beats a normal declaration, so it overwrote m27's entrance
     transform AND pushed each badge a further 12px / -0.5deg into the frame
     on every cycle — the resting position was never the authored one.
   · the badge is opaque, so whatever it lands on is gone, not dimmed.

   The two right-hand badges now sit entirely outside the frame — clear above
   the top-right corner, clear below the bottom-right corner. They annotate the
   screenshot instead of damaging it, and nothing inside the mock is hidden. */

/* opaque, no backdrop-filter: a badge that straddles the frame edge picks up
   two different backdrops and the blur draws a visible seam across it.
   animation:none — see above; the reveal is m27's transition, not a bob loop,
   and a bob loop would put the badges back over the chrome half the time. */
.h27 .float-badge {
  animation: none;
  backdrop-filter: none; -webkit-backdrop-filter: none;
  box-shadow: 0 6px 20px rgba(13,27,62,.12), 0 1px 3px rgba(13,27,62,.08);
}
.h27 .float-badge--success {
  top: auto; bottom: calc(100% + 12px); right: 4px; left: auto;
  background: #F0F5F2;
}
.h27 .float-badge--ai {
  top: calc(100% + 12px); bottom: auto; right: 4px; left: auto;
  background: #EBF4FB;
}
/* the sidebar's dead space below the nav is the one region with nothing to
   cover — this one can stay fully over the frame */
.h27 .float-badge--info {
  top: auto; bottom: 26%; left: -18px; right: auto; background: #EBF4FB;
}

/* The top badge needs a lane of its own between the intro copy and the frame.
   The generic stage gap is clamp(28px,4vw,56px), which is under the badge's
   ~40px + 12px at every width below ~1400px — without this the badge would
   trade one collision for another. */
.h27 #home-dashboard .h27-stage { margin-top: clamp(64px, 5.4vw, 82px); }

/* ── KPI strip · narrow widths ──────────────────────────────────────────
   Between 769px and ~1180px the strip is still four across but each tile is
   only 130–200px wide, so value + delta + viz can't all fit — the tile spills
   and the label ellipsises. Shed the optional parts in order: the basis text
   first, then the viz. Below 768px styles.css drops the grid to two columns
   and the tiles are wide again, so everything comes back. */

@media (max-width: 1180px) and (min-width: 769px) {
  .h27 .dashboard-v2__metric-spark,
  .h27 .dashboard-v2__metric-meter { max-width: 52px; min-width: 26px; }
}

@media (max-width: 1040px) and (min-width: 769px) {
  .h27 .dashboard-v2__metric { padding: 9px 10px 10px; }
  .h27 .dashboard-v2__metric-label { font-size: 7.5px; letter-spacing: .04em; }
  .h27 .dashboard-v2__metric-icon { width: 14px; height: 14px; border-radius: 4px; }
  .h27 .dashboard-v2__metric-val { font-size: 16px; }
  .h27 .dashboard-v2__delta { font-size: 8.5px; padding: 2px 5px; }
  .h27 .dashboard-v2__delta em { display: none; }
  .h27 .dashboard-v2__metric-spark,
  .h27 .dashboard-v2__metric-meter { display: none; }
}

/* ══ §10 · ACT 04 · MODULE INDEX — a full-width top tab bar drives one
       full-width stage below it. (Was a left rail + right stage; the rail
       squeezed the panel and the long labels wrapped badly.) ════════════ */

.h27 .h27-mod {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(20px, 2.4vw, 34px);
  align-items: start;
}
.h27 .h27-mod__list {
  border-top: 1px solid var(--h27-hair);
  border-bottom: 1px solid var(--h27-hair);
}
/* Nine tabs, edge to edge — equal columns so the row always covers the width.
   The 1px gap over a hairline background draws the dividers, so a wrapped row
   at a narrower breakpoint rules itself without any per-cell border juggling. */
.h27 .h27-mod .mod-tabs__nav {
  display: grid;
  grid-template-columns: repeat(9, minmax(0, 1fr));
  gap: 1px;
  width: 100%;
  margin: 0;
  justify-content: stretch;
  background: var(--h27-hair);
}

/* each pill is a stacked number-over-label tab cell */
.h27 .h27-mod .mod-tabs__pill {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: 6px;
  width: 100%;
  min-width: 0;
  /* styles.css sets `white-space: nowrap` for the old pill row; in a fixed-width
     tab cell that clipped "Practice management" against `overflow: hidden`. */
  white-space: normal;
  hyphens: none;
  padding: 15px 12px 17px;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: var(--h27-paper);
  text-align: left;
  cursor: pointer;
  font-family: 'Inter', sans-serif;
  font-size: clamp(12.5px, .96vw, 14.5px); font-weight: 600; letter-spacing: -.012em;
  line-height: 1.3;
  color: var(--h27-mute);
  overflow: hidden;
  transition: color 300ms var(--h27-ease), background-color 300ms var(--h27-ease);
}
/* hover/active wash rises from the bottom edge, under the coral rule */
.h27 .h27-mod .mod-tabs__pill::before {
  content: ''; position: absolute; inset: 0; z-index: 0;
  background: rgba(13,27,62,.055);
  transform: scaleY(0); transform-origin: 50% 100%;
  transition: transform 460ms var(--h27-ease);
}
.h27 .h27-mod .mod-tabs__pill > * { position: relative; z-index: 1; }
/* styles.css lifts and shadows the pill on hover — a tab cell in a ruled row
   must stay flush, so those have to be unset explicitly. */
.h27 .h27-mod .mod-tabs__pill:hover,
.h27 .h27-mod .mod-tabs__pill:not(.mod-tabs__pill--active):hover {
  color: var(--h27-navy);
  background: var(--h27-paper);
  border: 0;
  box-shadow: none;
  transform: none;
}
.h27 .h27-mod .mod-tabs__pill:hover::before { transform: scaleY(1); }

.h27 .h27-mod .h27-mod__no {
  font-family: 'Inter', sans-serif;
  font-size: 10.5px; font-weight: 800; letter-spacing: .12em;
  color: rgba(13,27,62,.3);
  transition: color 300ms var(--h27-ease);
}
/* the label sits under the number and is free to wrap inside its cell */
.h27 .h27-mod .mod-tabs__pill > span:not([class]) {
  display: block; width: 100%;
  white-space: normal;
  overflow-wrap: break-word;
  text-wrap: balance;
}
/* the arrow only reads in a vertical index — a tab cell has no room for it */
.h27 .h27-mod .h27-mod__go { display: none; }

/* the selected tab: coral rule along the bottom, navy label */
/* styles.css paints the selected pill navy with `color: #fff !important`; the
   tab wants the opposite, so the override has to match that weight. */
.h27 .h27-mod .mod-tabs__pill--active {
  color: var(--h27-navy) !important;
  background: var(--h27-paper);
  border: 0;
  box-shadow: none;
  transform: none;
  filter: none;
}
.h27 .h27-mod .mod-tabs__pill--active:hover,
.h27 .h27-mod .mod-tabs__pill--active:focus {
  background: var(--h27-paper);
  color: var(--h27-navy) !important;
  filter: none;
}
.h27 .h27-mod .mod-tabs__pill--active::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; top: auto;
  height: 3px; width: auto; z-index: 2;
  background: var(--h27-coral);
}
.h27 .h27-mod .mod-tabs__pill--active::before { transform: scaleY(1); }
.h27 .h27-mod .mod-tabs__pill--active .h27-mod__no { color: var(--h27-coral); }
/* the old pill icon chip is redundant in a tab cell */
.h27 .h27-mod .mod-tabs__pill-icon { display: none; }

/* the stage sits under the tab row, full width */
/* No card: the panel reads straight off the page under the tab row — the
   framed stage (border + radius + shadow + coral top rule) was a second box
   around content the tab row already encloses. */
.h27 .h27-mod__stage {
  width: 100%;
  position: relative;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  overflow: visible;
}
.h27 .h27-mod__stage::before { content: none; }
.h27 .h27-mod .module-panel {
  display: none;
  grid-template-columns: minmax(0, 1fr) minmax(0, .95fr);
  gap: clamp(24px, 3vw, 52px);
  padding: clamp(6px, 1vw, 14px) 0 0;
  margin: 0;
  background: transparent;
  border: 0; box-shadow: none;
}
.h27 .h27-mod .module-panel--active { display: grid; animation: h27-panel 620ms var(--h27-ease) both; }
/* styles.css still draws a 3px gradient hairline across the top of the info
   column — a leftover from the framed stage. Nothing frames the panel now, so
   the rule floats above the badge with no box to cap. */
.h27 .h27-mod .module-panel__info::before { content: none !important; }
@keyframes h27-panel {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.h27 .h27-mod .module-panel__badge {
  display: inline-block;
  font-family: 'Inter', sans-serif;
  font-size: 10.5px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  color: var(--h27-coral);
  background: none; border: 0; padding: 0; margin-bottom: 12px;
}
.h27 .h27-mod .module-panel__title {
  font-family: 'DM Serif Display', Georgia, serif !important;
  font-weight: 400;
  font-size: clamp(24px, 2.4vw, 34px); line-height: 1.08; letter-spacing: -.022em;
  color: var(--h27-navy); margin: 0 0 12px;
}
.h27 .h27-mod .module-panel__desc {
  font-size: 15px; line-height: 1.68; color: var(--h27-mute); margin-bottom: 20px;
}
/* styles.css renders this list as a two-column grid of card tiles. In an
   editorial index that reads as noise, so it is flattened back to a plain
   ruled list — `display` and the tile chrome both have to be unset explicitly. */
.h27 .h27-mod .module-panel__features {
  display: block;
  list-style: none; margin: 0 0 22px; padding: 0;
  border-top: 1px solid var(--h27-hair);
}
.h27 .h27-mod .module-panel__features li {
  display: block; position: relative;
  padding: 11px 0 11px 26px;
  border: 0;
  border-bottom: 1px solid var(--h27-hair);
  border-radius: 0;
  background: none;
  box-shadow: none;
  animation: none;
  overflow: visible;
}
.h27 .h27-mod .module-panel__features li:hover { transform: none; background: none; box-shadow: none; }
.h27 .h27-mod .module-panel__features li::before {
  content: ''; position: absolute; left: 1px; top: 17px;
  width: 11px; height: 6px; border-radius: 0;
  background: none;
  box-shadow: none;
  border-left: 2px solid var(--h27-coral);
  border-bottom: 2px solid var(--h27-coral);
  transform: rotate(-45deg);
}
.h27 .h27-mod .module-panel__features li::after { content: none; }
.h27 .h27-mod .module-panel__features li strong {
  display: block; font-family: 'Inter', sans-serif;
  font-size: 14px; font-weight: 700; color: var(--h27-navy); margin-bottom: 2px;
}
.h27 .h27-mod .module-panel__features li span {
  display: block; font-size: 13px; line-height: 1.55; color: var(--h27-mute);
}
/* The visual column keeps its navy plane (a deliberate contrast against the
   white stage) but loses the phone-notch pseudo-element and the squared-off
   right edge it inherits from the old card layout. */
.h27 .h27-mod .module-panel__visual {
  display: flex; align-items: center; justify-content: center;
  padding: clamp(18px, 1.8vw, 26px) !important;
  border-radius: 6px;
  border-left: 0 !important;
}
.h27 .h27-mod .module-panel__visual::after { content: none !important; }
/* Let the window fill its column — only the phone mock stays narrow. */
.h27 .h27-mod .module-mockup { max-width: none; margin: 0 auto; }
.h27 .h27-mod .module-mockup--phone { max-width: 250px; }
.h27 .h27-mod .module-panel__title::after { background: var(--h27-coral); width: 44px; }

/* ══ §11 · THE RELAY — one record, three phases, two struck-out seams ═════
   Replaces the old nine-column journey rail. That version put nine 12.5px
   labels in nine equal columns behind one decorative gradient rule: it read
   as a list of words, not as flow, and it never showed the thing the headline
   actually claims — that nothing changes hands.

   This version is built around the two SEAMS. A clinic running stitched
   software breaks in exactly two places: front desk -> the room, and the room
   -> billing. Somebody re-keys, somebody exports a file. So the layout puts
   those two moments on stage as physical columns between the phase lanes and
   strikes them out. The claim stops being an adjective and becomes the
   composition.

   Every stop also names the module that does the work, so the row doubles as
   the evidence for the 9 modules / 1 platform / 0 silos wall beneath it.

   MOTION (all CSS, no JS): a light runs down each lane's spine on a per-lane
   delay so the record visibly hands off left to right; the stops ignite in
   sequence (--s, 0..8) when the block reveals. The unlit state is armed only
   behind html.h27-ready, exactly like §20 — script off means everything is
   simply already lit.
   ══════════════════════════════════════════════════════════════════════════ */

.h27 .h27-relay { position: relative; margin-top: clamp(28px, 3.6vw, 52px); }

.h27 .h27-relay__flow {
  display: grid;
  grid-template-columns:
    minmax(0, 1fr) clamp(56px, 5vw, 80px)
    minmax(0, 1fr) clamp(56px, 5vw, 80px)
    minmax(0, 1fr);
  align-items: stretch;
}

/* ── the phase lane ───────────────────────────────────────────────────── */

.h27 .h27-lane {
  position: relative;
  display: flex; flex-direction: column;
  padding: clamp(20px, 2.1vw, 28px) clamp(16px, 1.7vw, 24px) clamp(15px, 1.6vw, 20px);
  background: var(--h27-paper);
  border: 1px solid var(--h27-hair);
  border-top: 0;
  border-radius: 7px;
  box-shadow: 0 22px 46px -40px rgba(13,27,62,.55);
}
/* the phase rule — the only place the lane colour runs at full strength */
.h27 .h27-lane::before {
  content: ''; position: absolute; inset-inline: -1px; top: 0; height: 3px;
  border-radius: 7px 7px 0 0;
  background: var(--lane);
}
.h27 .h27-lane--b { --lane: var(--h27-sky);   --lane-ink: #2C6FA6; --lane-soft: rgba(59,134,188,.10); }
.h27 .h27-lane--d { --lane: var(--h27-coral); --lane-ink: #C94A2D; --lane-soft: rgba(232,89,58,.09);  }
.h27 .h27-lane--a { --lane: var(--h27-sage);  --lane-ink: #4E7263; --lane-soft: rgba(122,158,142,.16); }

.h27 .h27-lane__head {
  display: flex; align-items: center; gap: 12px;
  padding-bottom: 13px;
  border-bottom: 1px solid var(--h27-hair);
}
.h27 .h27-lane__t {
  font-family: 'Inter', sans-serif;
  font-size: 11px; font-weight: 800; letter-spacing: .15em; text-transform: uppercase;
  color: var(--lane-ink);
  margin: 0;
}
.h27 .h27-lane__c {
  margin-left: auto; flex: 0 0 auto;
  padding: 3px 9px; border-radius: 999px;
  background: var(--lane-soft);
  font-family: 'Inter', sans-serif;
  font-size: 9.5px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--lane-ink);
}

/* ── the stops, strung on the lane spine ──────────────────────────────── */

.h27 .h27-lane__stops { position: relative; list-style: none; margin: 0; padding: 0; }

/* The spine doubles as the travelling light: the hairline is the background
   COLOUR, the moving segment is a no-repeat background IMAGE scrolled past it.
   One element, one animation, no extra node to keep in sync with the dots. */
.h27 .h27-lane__stops::before {
  content: ''; position: absolute; left: 17px; top: 26px; bottom: 26px; width: 2px;
  border-radius: 2px;
  background-color: var(--h27-hair);
  background-image: none;
  background-repeat: no-repeat;
  background-size: 100% 32%;
  background-position: 0 -45%;
  animation: h27-relay-run 3.6s linear infinite;
  animation-delay: var(--rd, 0s);
}
@keyframes h27-relay-run {
  0%   { background-position: 0 -45%; }
  100% { background-position: 0 145%; }
}

.h27 .h27-stop {
  position: relative;
  display: grid; grid-template-columns: 36px minmax(0, 1fr);
  gap: 13px;
  padding: 14px 0;
  border-bottom: 1px solid var(--h27-hair);
  transition: opacity 640ms var(--h27-ease), transform 640ms var(--h27-ease);
  transition-delay: calc(var(--s, 0) * 105ms);
}
.h27 .h27-stop:last-child { border-bottom: 0; padding-bottom: 4px; }

.h27 .h27-stop__dot {
  position: relative; z-index: 1;
  width: 36px; height: 36px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--lane-soft);
  box-shadow: inset 0 0 0 1.5px var(--lane);
  transition: background 520ms var(--h27-ease), box-shadow 520ms var(--h27-ease);
  transition-delay: calc(var(--s, 0) * 105ms);
}
.h27 .h27-stop__dot .ico {
  width: 17px; height: 17px; font-size: 17px;
  color: var(--lane-ink);
  transition: color 520ms var(--h27-ease);
  transition-delay: calc(var(--s, 0) * 105ms);
}

.h27 .h27-stop__body { min-width: 0; padding-top: 1px; }
/* hyphens are on site-wide; at 12.5px in a 300px column they break mid-word
   ("the patient con-firms") and the row stops reading as a caption */
.h27 .h27-stop__t,
.h27 .h27-stop__d { hyphens: none; -webkit-hyphens: none; }
.h27 .h27-stop__t {
  display: block;
  font-family: 'Inter', sans-serif;
  font-size: 14px; font-weight: 700; line-height: 1.3; letter-spacing: -.005em;
  color: var(--h27-navy);
  margin-bottom: 4px;
}
.h27 .h27-stop__d {
  display: block;
  font-family: 'Inter', sans-serif;
  font-size: 12.5px; line-height: 1.55; color: var(--h27-mute);
  margin-bottom: 9px;
}
/* the module credit — neutral by default so the lane colour stays scarce;
   only the leading pip carries the phase */
.h27 .h27-stop__mod {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 9px; border-radius: 3px;
  background: var(--h27-ice-2);
  font-family: 'Inter', sans-serif;
  font-size: 9.5px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--h27-navy);
  transition: background 320ms var(--h27-ease), color 320ms var(--h27-ease);
}
.h27 .h27-stop__mod::before {
  content: ''; flex: 0 0 auto;
  width: 4px; height: 4px; border-radius: 50%; background: var(--lane);
}

.h27 .h27-stop:hover .h27-stop__dot {
  background: var(--lane-ink);
  box-shadow: inset 0 0 0 1.5px var(--lane-ink), 0 10px 22px -10px var(--lane-ink);
  transition-delay: 0s;
}
.h27 .h27-stop:hover .h27-stop__dot .ico { color: #fff; transition-delay: 0s; }
.h27 .h27-stop:hover .h27-stop__mod { background: var(--lane-soft); color: var(--lane-ink); }

/* what the record carries into the next phase — pinned to the lane foot so
   lanes of two, four and three stops still bottom out level */
.h27 .h27-lane__carry {
  margin: auto 0 0; padding-top: 14px;
  border-top: 1px dashed var(--h27-hair);
  font-family: 'Inter', sans-serif;
  font-size: 11.5px; font-weight: 600; line-height: 1.5;
  color: var(--h27-body);
}
.h27 .h27-lane__carry span {
  display: block; margin-bottom: 4px;
  font-size: 9.5px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  color: var(--h27-mute);
}

/* ── the gap — where every other stack makes somebody re-type ──────────────
   Named `gap`, not `seam`: §1 already owns `.h27-seam` as the hairline rule
   between two same-tone acts, and that rule sets height:1px. ─────────────── */

.h27 .h27-gap {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px;
  padding: 8px 2px;
  text-align: center;
}
.h27 .h27-gap__rule {
  flex: 1 1 auto; min-height: 16px; width: 1.5px;
  background-image: linear-gradient(180deg, var(--h27-hair) 52%, transparent 52%);
  background-size: 1.5px 7px;
}
/* the "no" mark, drawn rather than iconised: a ring with a bar struck through */
.h27 .h27-gap__badge {
  position: relative; flex: 0 0 auto;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--h27-paper);
  animation: h27-gap-breathe 4.4s var(--h27-ease) infinite;
}
.h27 .h27-gap__badge::before,
.h27 .h27-gap__badge::after {
  content: ''; position: absolute; left: 50%; top: 50%;
}
.h27 .h27-gap__badge::before {
  width: 13px; height: 13px; border-radius: 50%;
  box-shadow: inset 0 0 0 1.6px var(--h27-coral);
  transform: translate(-50%, -50%);
}
.h27 .h27-gap__badge::after {
  width: 17px; height: 1.6px; border-radius: 1px;
  background: var(--h27-coral);
  transform: translate(-50%, -50%) rotate(-45deg);
}
@keyframes h27-gap-breathe {
  0%, 100% { box-shadow: inset 0 0 0 1.5px rgba(232,89,58,.40), 0 0 0 0 rgba(232,89,58,.18); }
  50%      { box-shadow: inset 0 0 0 1.5px rgba(232,89,58,.62), 0 0 0 9px rgba(232,89,58,0); }
}
.h27 .h27-gap__t {
  flex: 0 0 auto; max-width: 100px;
  font-family: 'Inter', sans-serif;
  font-size: 9px; font-weight: 800; letter-spacing: .11em; text-transform: uppercase;
  line-height: 1.45;
  color: var(--h27-coral-dk);
  hyphens: none; overflow-wrap: normal;
}

/* ── ignition: unlit only while the reveal engine is armed and the block is
      still off-screen. No script, no h27-ready class, nothing ever dims. ── */
html.h27-ready .h27 .h27-relay:not(.x27-in) .h27-stop {
  opacity: .42; transform: translate3d(0, 8px, 0);
}
html.h27-ready .h27 .h27-relay:not(.x27-in) .h27-stop__dot {
  background: var(--h27-paper);
  box-shadow: inset 0 0 0 1.5px var(--h27-hair);
}
html.h27-ready .h27 .h27-relay:not(.x27-in) .h27-stop__dot .ico { color: #B7C4D5; }

/* ══ §12 · NUMBER WALL — the proof act ═══════════════════════════════════ */

.h27 .h27-wall {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--h27-onink-hair);
  border-left: 1px solid var(--h27-onink-hair);
}
/* four-up variant (the "one journey" proof row) — declared after the base so
   it wins the equal-specificity tie */
.h27 .h27-wall--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.h27 .h27-wall--4 .h27-wall__num { font-size: clamp(38px, 4.6vw, 66px); }
.h27 .h27-act--paper .h27-wall,
.h27 .h27-act--ice .h27-wall { border-top-color: var(--h27-hair); border-left-color: var(--h27-hair); }
.h27 .h27-wall__cell {
  position: relative;
  padding: clamp(20px, 2.1vw, 30px) clamp(18px, 2vw, 32px);
  border-right: 1px solid var(--h27-onink-hair);
  border-bottom: 1px solid var(--h27-onink-hair);
  overflow: hidden;
  transition: background 460ms var(--h27-ease);
}
.h27 .h27-act--paper .h27-wall__cell,
.h27 .h27-act--ice .h27-wall__cell { border-right-color: var(--h27-hair); border-bottom-color: var(--h27-hair); }
.h27 .h27-wall__cell:hover { background: rgba(255,255,255,.04); }
.h27 .h27-act--paper .h27-wall__cell:hover { background: var(--h27-ice); }
.h27 .h27-wall__cell::after {
  content: ''; position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  background: var(--h27-coral);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 1100ms var(--h27-ease) 200ms;
}
.h27 .h27-wall__cell.x27-in::after { transform: scaleX(var(--h27-fill, .5)); }
.h27 .h27-wall__num {
  font-family: 'DM Serif Display', Georgia, serif !important; font-weight: 400;
  font-size: var(--h27-num);
  line-height: .88; letter-spacing: -.035em;
  color: #fff; margin-bottom: 10px;
}
.h27 .h27-act--paper .h27-wall__num,
.h27 .h27-act--ice .h27-wall__num { color: var(--h27-navy); }
.h27 .h27-wall__cell--coral .h27-wall__num { color: var(--h27-coral-lt); }
.h27 .h27-act--paper .h27-wall__cell--coral .h27-wall__num { color: var(--h27-coral); }
.h27 .h27-wall__lbl {
  font-family: 'Inter', sans-serif;
  font-size: 13.5px; line-height: 1.5; font-weight: 500;
  color: var(--h27-onink-mute);
  max-width: 26ch;
}
.h27 .h27-act--paper .h27-wall__lbl,
.h27 .h27-act--ice .h27-wall__lbl { color: var(--h27-mute); }

/* ══ §13 · GIANT MARQUEE — integrations, at display scale ════════════════ */

.h27 .h27-bigrun {
  position: relative;
  padding: clamp(13px, 1.5vw, 19px) 0;
  overflow: hidden;
  border-top: 1px solid var(--h27-onink-hair);
  border-bottom: 1px solid var(--h27-onink-hair);
}
.h27 .h27-bigrun__track {
  display: flex; align-items: center; gap: clamp(18px, 2vw, 30px);
  width: max-content;
  animation: h27-scroll 52s linear infinite;
}
.h27 .h27-bigrun__track--rev { animation-direction: reverse; animation-duration: 62s; }
.h27 .h27-bigrun:hover .h27-bigrun__track { animation-play-state: paused; }
.h27 .h27-bigrun__item {
  font-family: 'Inter', sans-serif;
  font-size: clamp(13px, 1.05vw, 16px);
  font-weight: 600;
  line-height: 1.3; letter-spacing: .005em;
  color: rgba(255,255,255,.42);
  white-space: nowrap;
  transition: color 340ms var(--h27-ease);
}
.h27 .h27-bigrun__item:hover { color: #fff; }
.h27 .h27-bigrun__sep {
  flex: 0 0 auto; width: 4px; height: 4px; border-radius: 50%;
  background: var(--h27-coral); opacity: .75;
}

/* ══ §14 · CARDS — one card language for the whole page ══════════════════ */

.h27 .h27-grid { display: grid; gap: clamp(14px, 1.6vw, 22px); }
.h27 .h27-grid--2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.h27 .h27-grid--3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.h27 .h27-grid--4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
.h27 .h27-span-2 { grid-column: span 2; }
.h27 .h27-span-3 { grid-column: span 3; }

.h27 .h27-card {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column;
  padding: clamp(24px, 2.4vw, 34px);
  border-radius: 6px;
  background: var(--h27-paper);
  box-shadow: inset 0 0 0 1px var(--h27-hair);
  text-decoration: none;
  transition: transform 460ms var(--h27-ease), box-shadow 460ms var(--h27-ease);
}
.h27 .h27-card:hover { transform: translateY(-6px); box-shadow: inset 0 0 0 1px rgba(13,27,62,.16), 0 22px 54px rgba(13,27,62,.12); }
.h27 .h27-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: var(--h27-coral);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 520ms var(--h27-ease);
}
.h27 .h27-card:hover::before { transform: scaleX(1); }
.h27 .h27-card h3 {
  font-family: 'Inter', sans-serif !important;
  font-size: var(--h27-d3); font-weight: 700; line-height: 1.16; letter-spacing: -.02em;
  color: var(--h27-navy); margin: 0 0 10px;
}
.h27 .h27-card p {
  font-size: 14.5px; line-height: 1.66; color: var(--h27-mute); margin: 0 0 16px;
}
.h27 .h27-card__foot { margin-top: auto; }
.h27 .h27-card__eyebrow {
  font-family: 'Inter', sans-serif;
  font-size: 10.5px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  color: var(--h27-coral); margin-bottom: 14px;
}
.h27 .h27-card__ico {
  width: 46px; height: 46px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--h27-ice-2);
  margin-bottom: 18px;
  transition: transform 460ms var(--h27-out), background 360ms var(--h27-ease);
}
.h27 .h27-card__ico svg { width: 22px; height: 22px; color: var(--h27-navy); stroke: var(--h27-navy); }
.h27 .h27-card:hover .h27-card__ico { transform: translateY(-3px) rotate(-4deg); }

/* dark card variant — one per grid, the lead */
.h27 .h27-card--ink {
  background: var(--h27-navy-up);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.1);
}
.h27 .h27-card--ink h3 { color: #fff; }
.h27 .h27-card--ink p  { color: var(--h27-onink); }
.h27 .h27-card--ink .h27-card__eyebrow { color: var(--h27-coral-lt); }
.h27 .h27-card--ink .h27-card__ico { background: rgba(255,255,255,.1); }
.h27 .h27-card--ink .h27-card__ico svg { color: #fff; stroke: #fff; }
.h27 .h27-card--ink:hover { box-shadow: inset 0 0 0 1px rgba(255,255,255,.2), 0 26px 60px rgba(0,0,0,.34); }
.h27 .h27-card--ink .h27-link { color: var(--h27-coral-lt); }

/* glass card — for dark acts where a solid white card would punch a hole */
.h27 .h27-card--glass {
  background: rgba(255,255,255,.08);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.h27 .h27-card--glass h3 { color: #fff; }
.h27 .h27-card--glass p  { color: var(--h27-onink); }
.h27 .h27-card--glass .h27-card__eyebrow { color: var(--h27-coral-lt); }
.h27 .h27-card--glass .h27-card__ico { background: rgba(255,255,255,.1); }
.h27 .h27-card--glass .h27-card__ico svg { color: #fff; stroke: #fff; }
.h27 .h27-card--glass .h27-card__arrow { color: #fff; }
.h27 .h27-card--glass:hover { box-shadow: inset 0 0 0 1px rgba(255,255,255,.24), 0 26px 60px rgba(0,0,0,.4); }

/* the arrow that lives at a card's bottom edge */
.h27 .h27-card__arrow {
  margin-top: auto; padding-top: 20px;
  font-size: 20px; line-height: 1; color: var(--h27-navy);
  transition: transform 420ms var(--h27-ease), color 300ms var(--h27-ease);
}
.h27 .h27-card--ink .h27-card__arrow { color: #fff; }
.h27 .h27-card:hover .h27-card__arrow { transform: translateX(8px); color: var(--h27-coral); }

/* ══ §15 · TABS (practice size) ══════════════════════════════════════════ */

.h27 .h27-tabs { display: flex; flex-wrap: wrap; gap: 0; border-bottom: 1px solid var(--h27-hair); margin-bottom: 0; }
.h27 .h27-tabs button {
  position: relative;
  padding: 16px 24px 16px 0; margin-right: 28px;
  border: 0; background: none; cursor: pointer;
  font-family: 'Inter', sans-serif;
  font-size: clamp(15px, 1.3vw, 19px); font-weight: 600; letter-spacing: -.015em;
  color: var(--h27-mute);
  transition: color 300ms var(--h27-ease);
}
.h27 .h27-tabs button::after {
  content: ''; position: absolute; left: 0; right: 24px; bottom: -1px; height: 2px;
  background: var(--h27-coral);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 420ms var(--h27-ease);
}
.h27 .h27-tabs button:hover { color: var(--h27-navy); }
.h27 .h27-tabs button[aria-selected="true"] { color: var(--h27-navy); }
.h27 .h27-tabs button[aria-selected="true"]::after { transform: scaleX(1); }

.h27 .h27-tabpanel { display: none; padding-top: clamp(24px, 3vw, 40px); }
.h27 .h27-tabpanel.is-active { display: block; animation: h27-panel 560ms var(--h27-ease) both; }
.h27 .h27-tabpanel p {
  font-size: clamp(17px, 1.5vw, 23px); line-height: 1.6; color: var(--h27-body);
  max-width: 60ch; margin: 0 0 24px;
}

/* pill row (roles) */
.h27 .h27-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.h27 .h27-pills a {
  font-family: 'Inter', sans-serif;
  font-size: 13px; font-weight: 600;
  padding: 10px 18px; border-radius: 999px;
  color: var(--h27-navy); text-decoration: none;
  box-shadow: inset 0 0 0 1px var(--h27-hair);
  transition: background 300ms var(--h27-ease), color 300ms var(--h27-ease), box-shadow 300ms var(--h27-ease);
}
.h27 .h27-pills a:hover { background: var(--h27-navy); color: #fff; box-shadow: inset 0 0 0 1px var(--h27-navy); }

/* ══ §16 · JOURNEY (migration) — a scroll-driven station line ════════════
   The old block was five flat columns under a static rule. The rollout is a
   sequence, so it is drawn as one: ONE line across the act with five stations
   standing on it. home-2027.js §10 maps the reader's scroll position through
   the block onto --jrn-p (0..1) — the persimmon fill runs to that point, a lit
   head travels with it, and each station lights the moment the line reaches it.

   The line deliberately runs past station 05 and fades out: the last step is
   "optimize as you go", so the journey should not visibly terminate.

   DEGRADATION follows the same contract as §11 and §20 — every default here is
   the FINISHED state, and the unlit states are armed only under `.is-live`,
   which the script adds after it has taken over. No script, an exception, or
   reduced motion all leave a fully drawn line with every station lit.

   Below 1024px the same component turns 90°: the rule becomes a left spine and
   the stations stack down it. No markup changes — the script picks the axis off
   the rail's own box. */

.h27 .h27-jrn {
  --jrn-p: 1;                                    /* scroll progress, 0..1 */
  --jrn-node: 42px;
  --jrn-line: calc(var(--jrn-node) / 2);         /* centre of the station row */
  position: relative;
  margin-top: clamp(30px, 3.6vw, 52px);
}

/* ── the line ─────────────────────────────────────────────────────────── */
.h27 .h27-jrn__rail {
  position: absolute; z-index: 0;
  left: 0; right: 0; top: calc(var(--jrn-line) - 1px);
  height: 2px; border-radius: 2px;
  background: var(--h27-hair);
  -webkit-mask-image: linear-gradient(90deg, #000 86%, transparent);
          mask-image: linear-gradient(90deg, #000 86%, transparent);
}
.h27 .h27-jrn__fill {
  position: absolute; inset: 0; border-radius: inherit;
  background: var(--h27-coral-dk);
  transform: scaleX(var(--jrn-p)); transform-origin: 0 50%;
  transition: transform 280ms var(--h27-out);
  will-change: transform;
}
/* the head of the line — sits outside the rail so the fade mask never eats it */
.h27 .h27-jrn__spark {
  position: absolute; z-index: 1;
  top: var(--jrn-line); left: calc(var(--jrn-p) * 100%);
  width: 8px; height: 8px; margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--h27-coral);
  box-shadow: 0 0 0 4px rgba(232,89,58,.13), 0 0 14px 2px rgba(232,89,58,.38);
  opacity: 0;
  transition: left 280ms var(--h27-out), top 280ms var(--h27-out), opacity 420ms var(--h27-ease);
}
/* It dissolves into the same tail the rail's mask fades out, so the head never
   floats detached past the end of the line. */
.h27 .h27-jrn.is-live .h27-jrn__spark { opacity: clamp(0, calc((0.94 - var(--jrn-p)) / 0.1), 1); }

/* ── the stations ─────────────────────────────────────────────────────── */
.h27 .h27-jrn__list {
  position: relative; z-index: 2;
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  column-gap: clamp(16px, 2vw, 30px);
}
.h27 .h27-jrn__step { position: relative; padding-right: clamp(4px, 1vw, 16px); }

/* station — opaque, so it punches a clean hole through the line. Nothing else
   is allowed on the line's row: a numeral sitting on the rule reads as struck
   through, so the numeral stacks under the station instead. */
.h27 .h27-jrn__node {
  position: relative; flex: none;
  margin-bottom: clamp(16px, 1.7vw, 24px);
  width: var(--jrn-node); height: var(--jrn-node);
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--h27-navy); color: #fff;
  box-shadow: inset 0 0 0 1.5px var(--h27-navy), 0 10px 22px -12px rgba(13,27,62,.6);
  transition: background 460ms var(--h27-ease), color 460ms var(--h27-ease),
              box-shadow 460ms var(--h27-ease), transform 560ms var(--h27-ease),
              opacity 340ms var(--h27-ease);
}
.h27 .h27-jrn__node .ico { font-size: 19px; width: 19px; height: 19px; }

/* the one station the line is standing on right now */
.h27 .h27-jrn__step.is-head .h27-jrn__node::after {
  content: ''; position: absolute; inset: -1px; border-radius: 50%;
  animation: h27JrnPulse 2.6s var(--h27-ease) infinite;
}
@keyframes h27JrnPulse {
  0%   { box-shadow: 0 0 0 0 rgba(232,89,58,.42); }
  72%  { box-shadow: 0 0 0 13px rgba(232,89,58,0); }
  100% { box-shadow: 0 0 0 0 rgba(232,89,58,0); }
}

.h27 .h27-jrn__no {
  display: block;
  font-family: 'DM Serif Display', Georgia, serif;
  font-size: clamp(28px, 3vw, 42px); line-height: 1; letter-spacing: -.03em;
  color: rgba(13,27,62,.32);
  margin-bottom: clamp(10px, 1.1vw, 14px);
  transition: color 480ms var(--h27-ease), opacity 340ms var(--h27-ease);
}

.h27 .h27-jrn__body { transition: opacity 340ms var(--h27-ease); }
.h27 .h27-jrn__step h3 {
  position: relative;
  font-family: 'Inter', sans-serif !important;
  font-size: clamp(15px, 1.3vw, 18.5px); font-weight: 700; letter-spacing: -.015em;
  color: var(--h27-navy); margin: 0 0 10px; padding-bottom: 11px;
}
.h27 .h27-jrn__step h3::after {
  content: ''; position: absolute; left: 0; bottom: 0; width: 26px; height: 2px;
  border-radius: 2px; background: var(--h27-coral);
  transform: scaleX(1); transform-origin: 0 50%;
  transition: transform 640ms var(--h27-ease);
}
.h27 .h27-jrn__step p { font-size: 13.5px; line-height: 1.62; color: var(--h27-mute); margin: 0; }

.h27 .h27-jrn__tag {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 15px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--h27-mute);
  transition: opacity 520ms var(--h27-ease), transform 520ms var(--h27-ease);
}
.h27 .h27-jrn__tag::before {
  content: ''; flex: none; width: 5px; height: 5px; border-radius: 50%;
  background: var(--h27-coral);
}

/* ── unlit: armed only while the script is actually driving the line ──── */
.h27 .h27-jrn.is-live .h27-jrn__step:not(.is-on) .h27-jrn__node {
  background: var(--h27-paper); color: #A9B8CC;
  box-shadow: inset 0 0 0 1.5px var(--h27-hair);
  transform: scale(.94);
}
.h27 .h27-jrn.is-live .h27-jrn__step:not(.is-on) .h27-jrn__no { color: rgba(13,27,62,.14); }
.h27 .h27-jrn.is-live .h27-jrn__step:not(.is-on) h3::after { transform: scaleX(0); }
.h27 .h27-jrn.is-live .h27-jrn__step:not(.is-on) .h27-jrn__tag {
  opacity: 0; transform: translate3d(0, 5px, 0);
}

/* ── pointer focus: the hovered station holds, its neighbours step back ── */
@media (hover: hover) and (pointer: fine) {
  .h27 .h27-jrn__list:hover .h27-jrn__step:not(:hover) .h27-jrn__node,
  .h27 .h27-jrn__list:hover .h27-jrn__step:not(:hover) .h27-jrn__no,
  .h27 .h27-jrn__list:hover .h27-jrn__step:not(:hover) .h27-jrn__body { opacity: .55; }
  .h27 .h27-jrn__step:hover .h27-jrn__node { transform: translate3d(0, -3px, 0) scale(1.06); }
  .h27 .h27-jrn__step:hover .h27-jrn__no { color: var(--h27-coral); }
}

/* ══ §17 · VOICES — testimonial wall ═════════════════════════════════════ */

.h27 .h27-quote {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column;
  padding: clamp(24px, 2.4vw, 36px);
  border-radius: 6px;
  background: var(--h27-paper);
  box-shadow: inset 0 0 0 1px var(--h27-hair);
  transition: transform 460ms var(--h27-ease), box-shadow 460ms var(--h27-ease);
}
.h27 .h27-quote:hover { transform: translateY(-5px); box-shadow: inset 0 0 0 1px rgba(13,27,62,.16), 0 20px 50px rgba(13,27,62,.1); }
.h27 .h27-quote::before {
  content: '\201C';
  position: absolute; top: -14px; right: 14px;
  font-family: 'DM Serif Display', Georgia, serif;
  font-size: 108px; line-height: 1; color: rgba(13,27,62,.06);
  pointer-events: none;
}
.h27 .h27-quote p {
  position: relative; z-index: 1;
  font-size: 15.5px; line-height: 1.7; color: var(--h27-body); margin: 0 0 20px;
}
.h27 .h27-quote__cite { margin-top: auto; padding-top: 16px; border-top: 1px solid var(--h27-hair); }
.h27 .h27-quote__n { font-family: 'Inter', sans-serif; font-weight: 700; font-size: 14.5px; color: var(--h27-navy); }
.h27 .h27-quote__r { font-family: 'Inter', sans-serif; font-size: 12.5px; color: var(--h27-mute); margin-top: 2px; }
.h27 .h27-quote--ink {
  background: var(--h27-navy-up);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.1);
}

/* On a dark act the whole wall flips to glass — a white card there would punch
   a hole, and the --ink lead card would disappear into the ground. */
.h27 .h27-act--ink .h27-quote,
.h27 .h27-act--void .h27-quote {
  background: rgba(255,255,255,.075);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.h27 .h27-act--ink .h27-quote--ink,
.h27 .h27-act--void .h27-quote--ink { box-shadow: inset 0 0 0 1px rgba(232,89,58,.42); }
.h27 .h27-act--ink .h27-quote::before,
.h27 .h27-act--void .h27-quote::before { color: rgba(255,255,255,.1); }
.h27 .h27-act--ink .h27-quote p,
.h27 .h27-act--void .h27-quote p { color: rgba(255,255,255,.88); }
.h27 .h27-act--ink .h27-quote__cite,
.h27 .h27-act--void .h27-quote__cite { border-top-color: rgba(255,255,255,.14); }
.h27 .h27-act--ink .h27-quote__n,
.h27 .h27-act--void .h27-quote__n { color: #fff; }
.h27 .h27-act--ink .h27-quote__r,
.h27 .h27-act--void .h27-quote__r { color: var(--h27-onink-mute); }
.h27 .h27-act--ink .h27-quote:hover,
.h27 .h27-act--void .h27-quote:hover {
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.26), 0 24px 56px rgba(0,0,0,.4);
}
.h27 .h27-act--ink .h27-railbtn,
.h27 .h27-act--void .h27-railbtn { color: #fff; box-shadow: inset 0 0 0 1px rgba(255,255,255,.24); }
.h27 .h27-act--ink .h27-railbtn:hover,
.h27 .h27-act--void .h27-railbtn:hover { background: #fff; color: var(--h27-navy); box-shadow: inset 0 0 0 1px #fff; }
.h27 .h27-quote--ink::before { color: rgba(255,255,255,.1); }
.h27 .h27-quote--ink p { color: rgba(255,255,255,.9); }
.h27 .h27-quote--ink .h27-quote__cite { border-top-color: rgba(255,255,255,.14); }
.h27 .h27-quote--ink .h27-quote__n { color: #fff; }
.h27 .h27-quote--ink .h27-quote__r { color: var(--h27-onink-mute); }

/* horizontal rail for the long testimonial list */
.h27 .h27-rail {
  display: flex; gap: 16px;
  overflow-x: auto; scroll-snap-type: x mandatory;
  padding-bottom: 10px;
  scrollbar-width: none;
}
.h27 .h27-rail::-webkit-scrollbar { display: none; }
.h27 .h27-rail > * { flex: 0 0 min(84vw, 380px); scroll-snap-align: start; }
.h27 .h27-railbtns { display: flex; gap: 8px; }
.h27 .h27-railbtn {
  width: 44px; height: 44px; border-radius: 50%;
  border: 0; cursor: pointer;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--h27-hair);
  color: var(--h27-navy);
  display: flex; align-items: center; justify-content: center;
  transition: background 300ms var(--h27-ease), color 300ms var(--h27-ease), box-shadow 300ms var(--h27-ease);
}
.h27 .h27-railbtn:hover { background: var(--h27-navy); color: #fff; box-shadow: inset 0 0 0 1px var(--h27-navy); }

/* ══ §18 · ACCORDION (FAQ) — full-bleed rules, big triggers ══════════════ */

.h27 .h27-acc { border-top: 1px solid var(--h27-hair); }
.h27 .h27-acc__item { border-bottom: 1px solid var(--h27-hair); }
.h27 .h27-acc__trigger {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  width: 100%; padding: clamp(20px, 2.2vw, 30px) 0;
  border: 0; background: none; cursor: pointer; text-align: left;
  font-family: 'Inter', sans-serif;
  font-size: clamp(16px, 1.6vw, 23px); font-weight: 600; letter-spacing: -.02em;
  color: var(--h27-navy);
  transition: color 280ms var(--h27-ease);
}
.h27 .h27-acc__trigger:hover { color: var(--h27-coral); }
.h27 .h27-acc__sign {
  position: relative; flex: 0 0 auto; width: 20px; height: 20px;
}
.h27 .h27-acc__sign::before,
.h27 .h27-acc__sign::after {
  content: ''; position: absolute; left: 50%; top: 50%; background: currentColor;
  transition: transform 420ms var(--h27-ease), opacity 300ms var(--h27-ease);
}
.h27 .h27-acc__sign::before { width: 16px; height: 1.5px; transform: translate(-50%,-50%); }
.h27 .h27-acc__sign::after  { width: 1.5px; height: 16px; transform: translate(-50%,-50%); }
.h27 .h27-acc__trigger[aria-expanded="true"] .h27-acc__sign::after { transform: translate(-50%,-50%) scaleY(0); opacity: 0; }
.h27 .h27-acc__trigger[aria-expanded="true"] { color: var(--h27-coral); }
.h27 .h27-acc__panel {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows 520ms var(--h27-ease);
}
.h27 .h27-acc__panel > div { overflow: hidden; }
.h27 .h27-acc__item.is-open .h27-acc__panel { grid-template-rows: 1fr; }
.h27 .h27-acc__panel p {
  font-size: clamp(15px, 1.15vw, 17px); line-height: 1.72; color: var(--h27-mute);
  margin: 0 0 clamp(20px, 2.2vw, 30px); max-width: 68ch;
}

/* ══ §19 · CLOSING CTA — full-bleed statement ════════════════════════════
   Kept byte-for-byte in step with platinum-theme.css §19, which is the same
   band for every other page. This file loads after the theme on the homepage
   only, so any value left at its old size here would silently un-compact the
   homepage close alone — edit both or neither. See that file for the reasoning
   behind each number. */

.h27 .h27-close {
  position: relative; overflow: hidden;
  background: var(--h27-void); color: #fff;
  padding-block: clamp(52px, 5.6vw, 88px);
  text-align: center;
  isolation: isolate;
}
.h27 .h27-close h2 {
  font-size: clamp(27px, 3.4vw, 46px);
  line-height: 1.06; letter-spacing: -.022em;
  color: #fff; margin: 0 0 12px;
  text-wrap: balance;
}
.h27 .h27-close .h27-kicker {
  font-family: 'Inter', sans-serif;
  font-size: clamp(12px, .85vw, 14px); font-weight: 600; letter-spacing: .04em;
  color: var(--h27-coral-lt); margin-bottom: 10px;
}
.h27 .h27-close p {
  font-size: clamp(15.5px, 1.05vw, 17.5px); line-height: 1.6;
  color: var(--h27-onink);
  margin: 0 auto 24px; max-width: 56ch;
  hyphens: none; -webkit-hyphens: none;
  text-wrap: pretty;
}
.h27 .h27-close .h27-cta-row { justify-content: center; }
.h27 .h27-close__proof {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 22px;
  margin-top: 26px; padding-top: 18px;
  border-top: 1px solid var(--h27-onink-hair);
}
.h27 .h27-close__proof span {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: 'Inter', sans-serif;
  font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--h27-onink-mute);
}
.h27 .h27-close__proof span::before {
  content: ''; width: 4px; height: 4px; border-radius: 50%; background: var(--h27-coral);
}
@media (max-width: 640px) {
  .h27 .h27-close { padding-block: 48px; }
  .h27 .h27-close p { margin-bottom: 22px; max-width: 40ch; }
  .h27 .h27-close__proof { margin-top: 22px; padding-top: 16px; gap: 8px 18px; }
}

/* ══ §20 · MOTION ════════════════════════════════════════════════════════ */

/* The starting (hidden) states are gated behind html.h27-ready, which home-2027.js
   sets only once its IntersectionObserver exists. If that script never runs — or
   throws — nothing is ever hidden, so the page degrades to fully visible. */
html.h27-ready .h27 [data-h27] { opacity: 0; will-change: opacity, transform; }
html.h27-ready .h27 [data-h27="up"]    { transform: translate3d(0, 26px, 0); }
html.h27-ready .h27 [data-h27="rise"]  { transform: translate3d(0, 42px, 0); }
html.h27-ready .h27 [data-h27="left"]  { transform: translate3d(-30px, 0, 0); }
html.h27-ready .h27 [data-h27="right"] { transform: translate3d(30px, 0, 0); }
html.h27-ready .h27 [data-h27="mask"]  { clip-path: inset(0 0 100% 0); transform: none; }
html.h27-ready .h27 [data-h27="fade"]  { transform: none; }
.h27 [data-h27] {
  transition:
    opacity 780ms var(--h27-ease),
    transform 880ms var(--h27-ease),
    clip-path 980ms var(--h27-ease);
  transition-delay: calc(var(--h27-i, 0) * 80ms);
}
/* The revealed state has to carry the same `html.h27-ready` prefix as the hidden
   states above — without it the per-variant rules (which are one element more
   specific) win and the element stays at opacity 0 forever. */
.h27 [data-h27].x27-in,
html.h27-ready .h27 [data-h27].x27-in {
  opacity: 1; transform: none;
}
/* …but NOT clip-path. Only "mask" animates from inset(0 0 100% 0) and so needs
   an end state; on every other variant the blanket clip was pure cost — it
   pinned the element to its border box for good. §33's dashboard-stage rule and
   module-2027 §9's badge rule are two hand-patched symptoms of that; the third
   was display type, which sets line-height .92 and therefore hangs the last
   line's descenders below the box. Scoping the reset fixes the class. */
html.h27-ready .h27 [data-h27="mask"].x27-in { clip-path: inset(0 0 0 0); }
html.h27-ready .h27 [data-h27="mask"].x27-in.x27-done { clip-path: none; }

@media (prefers-reduced-motion: reduce) {
  .h27 *, .h27 *::before, .h27 *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .h27 [data-h27] { opacity: 1 !important; transform: none !important; clip-path: none !important; }
  .h27 .h27-ticker__track, .h27 .h27-bigrun__track { animation: none !important; }
  .h27 .h27-hero__video { display: none; }
  /* the console never goes live here (home-2027.js §9 bails first), so the
     server-rendered answered frame is what stays — make sure all of it shows */
  .h27 .h27-ail__mesh { animation: none !important; }
  .h27 .h27-ail__out li, .h27 .h27-ail__trace li,
  .h27 .h27-ail__reply span, .h27 .h27-ail__act, .h27 .h27-ail__foot,
  .h27 .h27-ail__turn { opacity: 1 !important; transform: none !important; animation: none !important; }
  /* the script never starts here, so the one server-rendered turn is the whole
     panel — it should stand at its natural height rather than in a scroller */
  .h27 .h27-ail__thread { max-height: none; overflow: visible; -webkit-mask-image: none; mask-image: none; }
}

/* ══ §21 · RESPONSIVE ════════════════════════════════════════════════════ */

@media (max-width: 1240px) {
  /* nine across gets too tight for "Practice management" — wrap to 5 + 4 */
  .h27 .h27-mod .mod-tabs__nav { grid-template-columns: repeat(5, minmax(0,1fr)); }
}

@media (max-width: 1400px) {
  /* the chart pane needs the whole window here — keeping the schedule column
     wraps the coverage strip, the codes and the scribe bar onto second lines */
  .h27 .h27-hs__pane { grid-template-columns: minmax(0, 1fr); }
  .h27 .h27-hs__side { display: none; }
}

/* The relay stops being a triptych before the lanes get too narrow to hold a
   description line. It stacks, and the gaps turn from vertical columns into
   horizontal ones — the struck mark keeps doing the same job either way. The
   three stops in a lane then run across instead of down. */
@media (max-width: 1180px) {
  .h27 .h27-relay__flow { grid-template-columns: minmax(0, 1fr); }
  .h27 .h27-gap { flex-direction: row; gap: 14px; padding: 16px 0; }
  .h27 .h27-gap__rule {
    width: auto; height: 1.5px; min-height: 0; min-width: 20px;
    background-image: linear-gradient(90deg, var(--h27-hair) 52%, transparent 52%);
    background-size: 7px 1.5px;
  }
  .h27 .h27-gap__t { max-width: none; }
  .h27 .h27-lane__stops {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: clamp(18px, 2.6vw, 36px);
  }
  .h27 .h27-lane__stops::before { display: none; }
  .h27 .h27-stop { border-bottom: 0; }
  .h27 .h27-lane__carry { margin-top: 4px; padding-top: 16px; }
}

/* one column again — and with it the spine and the travelling light, which is
   the whole point of the block and reads best in a single file */
@media (max-width: 780px) {
  .h27 .h27-lane__stops { display: block; }
  .h27 .h27-lane__stops::before { display: block; }
  .h27 .h27-stop { border-bottom: 1px solid var(--h27-hair); }
  .h27 .h27-stop:last-child { border-bottom: 0; }
}

/* The station line turns 90° rather than wrapping to a second row — a rule with
   five stops only reads as one journey while it is a single unbroken line. Same
   markup, same script: the axis is inferred from the rail's own box. */
@media (max-width: 1024px) {
  .h27 .h27-jrn__rail {
    left: calc(var(--jrn-line) - 1px); right: auto;
    top: var(--jrn-line); bottom: 0;
    width: 2px; height: auto;
    -webkit-mask-image: linear-gradient(180deg, #000 88%, transparent);
            mask-image: linear-gradient(180deg, #000 88%, transparent);
  }
  .h27 .h27-jrn__fill { transform: scaleY(var(--jrn-p)); transform-origin: 50% 0; }
  .h27 .h27-jrn__spark {
    left: var(--jrn-line);
    top: calc(var(--jrn-line) + var(--jrn-p) * (100% - var(--jrn-line)));
  }
  .h27 .h27-jrn__list {
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(26px, 4vw, 40px);
  }
  .h27 .h27-jrn__step {
    display: grid;
    grid-template-columns: var(--jrn-node) minmax(0, 1fr);
    column-gap: clamp(16px, 2.4vw, 26px);
    align-items: start;
    padding-right: 0;
  }
  .h27 .h27-jrn__node { margin-bottom: 0; }
  /* the numeral now heads its own entry, so it drops to the station's baseline */
  .h27 .h27-jrn__no {
    font-size: clamp(26px, 5vw, 34px);
    margin-bottom: clamp(8px, 1.4vw, 12px);
  }
  .h27 .h27-jrn__body { padding-top: 3px; }
  .h27 .h27-jrn__step p { max-width: 62ch; }
}

@media (max-width: 1100px) {
  /* the hero gutter is too narrow here for the proof cards to hang outside —
     tuck them back over the glass instead of letting them reach the headline */
  .h27 .h27-hs__float--claim { top: -4%; right: -3%; }
  .h27 .h27-hs__float--pay   { bottom: -10%; left: -2%; }
  .h27 .h27-hs__fnum { font-size: 21px; }
}

@media (max-width: 900px) {
  .h27 .h27-head { grid-template-columns: minmax(0,1fr); gap: 20px; align-items: start; }
  .h27 .h27-hero { min-height: 0; padding-top: clamp(76px, 11vh, 104px); }
  .h27 .h27-hero__grid { grid-template-columns: minmax(0,1fr); padding-bottom: 44px; }
  .h27 .h27-hero__stage { display: none; }
  .h27 .h27-rail-grid { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .h27 .h27-rail-cell:nth-child(3) { border-left: 0; padding-left: 0; }
  .h27 .h27-rail-cell:nth-child(n+3) { border-top: 1px solid rgba(255,255,255,.11); }
  .h27 .h27-split { grid-template-columns: minmax(0,1fr); }
  .h27 .h27-split__side { border-left: 0; border-top: 1px solid var(--h27-hair); }
  .h27 .h27-split__side:first-child { border-top: 0; }
  .h27 .h27-os__head { flex-direction: column; align-items: flex-start; gap: 14px; }
  .h27 .h27-os__say { max-width: none; margin-bottom: 0; }
  /* the panels give their side padding back to the rail — at this width the
     inset gutter costs more label room than the surface edge is worth */
  .h27 .h27-os__row { padding-inline: 0; }
  /* two up, and the tile goes back beside the label — stacked cells at this
     width leave a wide empty gutter under every icon */
  .h27 .h27-os__cells { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .h27 .h27-os__cell {
    flex-direction: row; align-items: center; gap: 13px;
    padding: 15px 38px 17px 16px;
  }
  .h27 .h27-os__cell:nth-child(odd) { border-left: 0; padding-left: 0; }
  .h27 .h27-os__cell:nth-child(n+3) { border-top: 1px solid var(--os-hair); }
  /* Run's odd fifth cell takes the whole line — left in one column its top
     rule stopped at the midpoint and left a ragged half-hairline */
  .h27 .h27-os__row--run .h27-os__cell:nth-child(5) { grid-column: 1 / -1; }
  .h27 .h27-os__no { top: 50%; right: 4px; transform: translateY(-50%); }
  .h27 .h27-os__tile { flex: 0 0 auto; width: 36px; height: 36px; margin-bottom: 0; }
  .h27 .h27-ail__grid { grid-template-columns: minmax(0,1fr); gap: 36px; }
  /* single column: the console has the full measure, so the frame gets the
     height a whole turn needs rather than the one a half-width column allowed */
  .h27 .h27-ail__thread { max-height: 424px; }
  .h27 .h27-ail__win.is-live .h27-ail__thread { height: 424px; }
  .h27 .h27-mod .mod-tabs__nav { grid-template-columns: repeat(3, minmax(0,1fr)); }
  .h27 .h27-mod .module-panel { grid-template-columns: minmax(0,1fr); }
  .h27 .h27-wall { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .h27 .h27-grid--3, .h27 .h27-grid--4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .h27 .h27-span-2, .h27 .h27-span-3 { grid-column: span 2; }
  /* Stays top-right: pinned to the bottom it lands on top of the proof rail
     and clips the last cell's label. */
  .h27 .h27-hero__vtoggle { top: 88px; right: 14px; bottom: auto; width: 36px; height: 36px; }
}

@media (max-width: 640px) {
  .h27 { --h27-act: 62px; }
  .h27 .h27-wall { grid-template-columns: minmax(0,1fr); }
  .h27 .h27-grid--2, .h27 .h27-grid--3, .h27 .h27-grid--4 { grid-template-columns: minmax(0,1fr); }
  .h27 .h27-span-2, .h27 .h27-span-3 { grid-column: span 1; }
  .h27 .h27-jrn { --jrn-node: 38px; }
  .h27 .h27-jrn__node .ico { font-size: 17px; width: 17px; height: 17px; }
  .h27 .h27-strip { flex-direction: column; align-items: flex-start; }
  .h27 .h27-btn { width: 100%; justify-content: center; }
  .h27 .h27-cta-row { width: 100%; }
  .h27 .h27-tabs button { margin-right: 18px; padding-right: 0; font-size: 15px; }
  .h27 .h27-tabs button::after { right: 0; }
  .h27 .h27-mod .module-panel { padding: 14px 0 0; }
  .h27 .h27-mod .mod-tabs__nav { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .h27 .h27-mod .mod-tabs__pill { padding: 12px 12px 14px; font-size: 13px; }
  .h27 .h27-os__cells { grid-template-columns: minmax(0,1fr); }
  .h27 .h27-os__cell { border-left: 0; padding-left: 0; }
  .h27 .h27-os__cell:nth-child(n+2) { border-top: 1px solid var(--os-hair); }
  .h27 .h27-ail__win { padding: 16px 14px; }
  .h27 .h27-ail__ask { padding-left: 13px; gap: 9px; }
  .h27 .h27-ail__out li { padding: 11px 12px; column-gap: 11px; }
  /* the count chip has nowhere to sit next to the title at this width — it
     drops to its own row under the copy rather than squeezing the headline */
  .h27 .h27-ail__out li { grid-template-columns: 28px minmax(0, 1fr); }
  .h27 .h27-ail__meta { grid-column: 2; grid-row: 3; justify-self: start; margin-top: 2px; }
  .h27 .h27-ail__tick { width: 28px; height: 28px; }
  .h27 .h27-ail__trace li { font-size: 12px; }
  .h27 .h27-ail__foot { font-size: 12px; }
  .h27 .h27-ail__corner { width: 12px; height: 12px; }
  /* the answer column loses the 26px mark gutter it cannot afford — the
     bubble alignment already says which side of the conversation it is */
  .h27 .h27-ail__bot { grid-template-columns: minmax(0, 1fr); gap: 9px; }
  .h27 .h27-ail__mark { display: none; }
  .h27 .h27-ail__you p { max-width: 92%; font-size: 13px; }
  /* the rows go two-line and the chip drops to its own line at this width, so
     a whole turn is ~150px taller than it is on a desktop column */
  .h27 .h27-ail__thread { max-height: 496px; }
  .h27 .h27-ail__win.is-live .h27-ail__thread { height: 496px; }
  .h27 .h27-ail__reply { font-size: 13px; }
  /* the source chip wraps under its step rather than crushing the step text */
  .h27 .h27-ail__trace li { flex-wrap: wrap; row-gap: 3px; }
  .h27 .h27-ail__tool { margin-left: 23px; }
  .h27 .h27-ail__pill { font-size: 11.5px; padding: 6px 11px; }
  .h27 .h27-ail__wait { margin-left: 0; }
}

/* SURFACE-2027 fit: compact hero on small phones so the first screen covers cleanly */
@media (max-width: 560px) {
  .h27 .h27-hero { padding-top: 56px; }
  .h27 .h27-hero h1 { margin-bottom: 12px; }
  .h27 .h27-hero__sub { margin-bottom: 16px; }
  .h27 .h27-hero__grid { gap: 14px; padding-bottom: 12px; }
  .h27 .h27-hero__stage { transform: scale(.84); transform-origin: top center; margin-bottom: -9%; }
}

/* SURFACE-2027 fit: short laptop viewports (e.g. 1366x768) — tighten the hero vertically.
   The type and the stage shrink; the SECTION HEIGHT does not. `min-height: 0`
   used to sit here too, and it is what made the short-laptop first screen read
   as a different (worse) page: with the hero collapsed to its content the band
   below rose ~80px, so the fold landed halfway down the section-index bar and
   the first screen ended on a row of half-cut tab labels. The base rule —
   min(100vh - 128px, 860px) — is what puts the stat rail on the fold with the
   clinic ticker just peeking under it, which is the approved opening on tall
   screens. It reads correctly at every height, so the tier keeps it. */
@media (min-width: 900px) and (max-height: 880px) {
  .h27 .h27-hero { padding-top: 62px; }
  .h27 .h27-hero h1 { font-size: clamp(32px, 4.2vw, 53px); margin-bottom: 12px; }
  .h27 .h27-hero__sub { margin-bottom: 18px; }
  .h27 .h27-hero__grid { padding-bottom: 18px; }
  .h27 .h27-hero__stage { transform: scale(.88); transform-origin: center; }
}

/* second tier for the shortest laptops (<=800px tall) */
@media (min-width: 900px) and (max-height: 800px) {
  .h27 .h27-hero { padding-top: 54px; }
  .h27 .h27-hero h1 { font-size: clamp(30px, 3.8vw, 47px); }
  .h27 .h27-hero__stage { transform: scale(.82); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   §15 · SURFACE RHYTHM  —  "one ground per act, never twice in a row"
   ---------------------------------------------------------------------------
   Client note: the page ran every light act on the same two surfaces
   (#FFFFFF and the ice #F7FAFD, which read as the same colour), so ten
   different arguments all looked like one long white page.

   This layer gives each act its own GROUND and its own TEXTURE, so the eye
   registers a new chapter before it reads a word. Two rules govern it:

     1. no two neighbouring bands share a ground — the sequence steps
        paper → mist → paper → sage → navy → steel → paper → mist → ice …
     2. no ground is invented — every tint is the locked brand navy / sky /
        sage mixed into white at 3–8%. No new hues, no warm stone, and
        persimmon still only appears as an accent, never as a surface.

   ADDITIVE BY DESIGN: the tone classes (--paper/--ice/--ink/--void) stay on
   the sections, so every existing `.h27-act--ice .thing` rule keeps working.
   `.h27-g--*` only repaints the ground (2 classes, so it out-specifies the
   tone class without !important) and re-scopes --h27-hair so hairlines keep
   the same optical weight on a tinted surface as they had on white.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── §15a · GROUNDS ──────────────────────────────────────────────────────── */

.h27 {
  /* light grounds — sky / sage / navy dissolved into white */
  --h27-mist:    #F4F9FD;   /* sky 3%  — the product bands */
  --h27-mist-2:  #E9F2FB;
  --h27-sage-g:  #F1F6F3;   /* sage 5% — the human / journey bands */
  --h27-sage-g2: #E6EFE9;
  --h27-steel:   #EDF3FA;   /* navy 6% — the one mid-tone step */
  --h27-steel-2: #E0EBF7;
}

/* a tinted band names its own top edge — a hairline, not a hard butt joint */
.h27 .h27-act[class*="h27-g--"]:not(.h27-g--navy) {
  box-shadow: inset 0 1px 0 rgba(13,27,62,.055), inset 0 -1px 0 rgba(13,27,62,.045);
}

.h27 .h27-act.h27-g--mist {
  background: var(--h27-mist);
  --h27-hair: #D9E5F3;
}
.h27 .h27-act.h27-g--sage {
  background: var(--h27-sage-g);
  --h27-hair: #D5E1DA;
}
.h27 .h27-act.h27-g--steel {
  background: var(--h27-steel);
  --h27-hair: #CEDCEC;
}
.h27 .h27-act.h27-g--ice {
  background: var(--h27-ice);
  --h27-hair: #DDE7F2;
}

/* the one raised dark plane — navy lifted off the ink so the four dark bands
   are not four identical black rectangles either */
.h27 .h27-act.h27-g--navy {
  background:
    var(--h27-navy-up);
}

/* ── §15b · TEXTURE LAYER ────────────────────────────────────────────────
   One <div class="h27-tex h27-tex--x" aria-hidden="true"> per band, sitting
   at z-index 0 (the .h27-edge content is z-index 2, so it always stays on
   top). The element itself carries a paper grain; ::before carries the
   pattern; ::after carries the atmosphere. Every pattern is masked so it
   fades out rather than tiling edge to edge like wallpaper. */

.h27 .h27-tex {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none; overflow: hidden;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.86' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23p)' opacity='.055'/%3E%3C/svg%3E");
}
.h27 .h27-tex--flat { background-image: none; }
.h27 .h27-tex::before,
.h27 .h27-tex::after { content: ''; position: absolute; }

/* (1) BLUEPRINT — engineered graph paper. Act 01: the argument is about
       systems that do not line up, so the ground is literally ruled. */
.h27 .h27-tex--grid::before {
  inset: -2px;
  background-image:
    linear-gradient(rgba(13,27,62,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(13,27,62,.045) 1px, transparent 1px),
    linear-gradient(rgba(13,27,62,.085) 1px, transparent 1px),
    linear-gradient(90deg, rgba(13,27,62,.085) 1px, transparent 1px);
  background-size: 34px 34px, 34px 34px, 170px 170px, 170px 170px;
  -webkit-mask-image: radial-gradient(115% 92% at 86% -6%, #000 0%, rgba(0,0,0,.5) 44%, transparent 78%);
          mask-image: radial-gradient(115% 92% at 86% -6%, #000 0%, rgba(0,0,0,.5) 44%, transparent 78%);
}
.h27 .h27-tex--grid::after {
  inset: -12% -6% auto -20%; height: 62%;
  background: none;
}

/* (2) DOT MATRIX — the module index. Nine discrete things, on a field of
       discrete marks. */
.h27 .h27-tex--dots::before {
  inset: 0;
  background-image: radial-gradient(rgba(13,27,62,.14) 1.15px, transparent 1.15px);
  background-size: 23px 23px;
  -webkit-mask-image: radial-gradient(88% 76% at 8% 102%, #000 0%, transparent 74%);
          mask-image: radial-gradient(88% 76% at 8% 102%, #000 0%, transparent 74%);
}
.h27 .h27-tex--dots::after {
  inset: -18% -14% auto auto; width: 58%; height: 74%;
  background: none;
}

/* (3) CONTOUR — concentric survey rings. The journey act: one path, mapped. */
.h27 .h27-tex--topo::before {
  inset: -30% -10%;
  background-image: repeating-radial-gradient(circle at 80% 44%,
    transparent 0 27px, rgba(13,27,62,.055) 27px 28px);
  -webkit-mask-image: radial-gradient(64% 82% at 82% 44%, #000 8%, transparent 76%);
          mask-image: radial-gradient(64% 82% at 82% 44%, #000 8%, transparent 76%);
}
.h27 .h27-tex--topo::after {
  inset: auto auto -22% -16%; width: 62%; height: 70%;
  background: none;
}

/* (4) HATCH — 45° hairlines. The number wall: a measured, drafted surface. */
.h27 .h27-tex--rule::before {
  inset: 0;
  background-image: repeating-linear-gradient(45deg,
    rgba(13,27,62,.05) 0 1px, transparent 1px 12px);
  -webkit-mask-image: linear-gradient(102deg, #000 0%, rgba(0,0,0,.42) 40%, transparent 74%);
          mask-image: linear-gradient(102deg, #000 0%, rgba(0,0,0,.42) 40%, transparent 74%);
}
.h27 .h27-tex--rule::after {
  inset: -20% -12% auto auto; width: 64%; height: 86%;
  background: none;
}

/* (5) BEAM — a lit stage. The product act: light falls on the screenshot
       from above, the way a product shot is lit. */
.h27 .h27-tex--beam::before {
  inset: 0;
  background:
    linear-gradient(90deg, rgba(13,27,62,.035) 1px, transparent 1px), linear-gradient(rgba(13,27,62,.035) 1px, transparent 1px);
  background-size: auto, 88px 88px, 88px 88px;
  -webkit-mask-image: radial-gradient(96% 82% at 50% 12%, #000 0%, transparent 82%);
          mask-image: radial-gradient(96% 82% at 50% 12%, #000 0%, transparent 82%);
}
.h27 .h27-tex--beam::after {
  inset: -14% -8%;
  background:
    none;
  animation: h27-tex-drift 46s var(--h27-ease) infinite alternate;
}

/* (6) ARC — quarter rings radiating from the corner. The switching act:
       one origin, everything moving out from it. */
.h27 .h27-tex--arc::before {
  inset: -20%;
  background-image: repeating-radial-gradient(circle at 8% 94%,
    transparent 0 48px, rgba(13,27,62,.055) 48px 49px);
  -webkit-mask-image: radial-gradient(74% 78% at 8% 94%, #000 14%, transparent 78%);
          mask-image: radial-gradient(74% 78% at 8% 94%, #000 14%, transparent 78%);
}
.h27 .h27-tex--arc::after {
  inset: -16% -12% auto auto; width: 60%; height: 78%;
  background: none;
}

/* (7) MESH — atmosphere only, no pattern. For bands whose content already
       carries a lot of ruled structure (tabs, cards) and would fight one. */
.h27 .h27-tex--mesh::before {
  inset: -18% -10%;
  background:
    none;
  animation: h27-tex-drift 52s var(--h27-ease) infinite alternate;
}
.h27 .h27-tex--mesh::after {
  inset: 0;
  background-image: radial-gradient(rgba(13,27,62,.08) 1px, transparent 1px);
  background-size: 40px 40px;
  -webkit-mask-image: radial-gradient(80% 60% at 50% 100%, #000 0%, transparent 72%);
          mask-image: radial-gradient(80% 60% at 50% 100%, #000 0%, transparent 72%);
}

/* (8) RAILS — two long margin rules, like a ruled notebook column. The FAQ
       is a reading band, so its texture is typographic, not graphic. */
.h27 .h27-tex--rails::before {
  inset: 8% 0;
  background-image:
    linear-gradient(90deg, rgba(13,27,62,.11) 1px, transparent 1px),
    linear-gradient(270deg, rgba(13,27,62,.11) 1px, transparent 1px);
  /* outside the h27-edge--tight reading column, so they frame it rather than
     ruling lines through the accordion rows */
  background-position: 9% 0, 91% 0;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 22%, #000 78%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 22%, #000 78%, transparent 100%);
}
.h27 .h27-tex--rails::after {
  inset: auto -10% -24% -10%; height: 58%;
  background: none;
}

@keyframes h27-tex-drift {
  0%   { transform: translate3d(0,0,0) scale(1); }
  100% { transform: translate3d(-2.2%, 1.8%, 0) scale(1.05); }
}
@media (prefers-reduced-motion: reduce) {
  .h27 .h27-tex::before,
  .h27 .h27-tex::after { animation: none !important; }
}

/* ── §15c · GROUND-AWARE COMPONENTS ──────────────────────────────────────
   The handful of things that assumed a white or ice ground and have to
   follow the band they now sit on. */

/* the number wall's hover fill has to go LIGHTER than steel, not to ice */
.h27 .h27-act.h27-g--steel .h27-wall__cell:hover,
.h27 .h27-act.h27-g--mist  .h27-wall__cell:hover,
.h27 .h27-act.h27-g--sage  .h27-wall__cell:hover { background: rgba(255,255,255,.62); }

/* icon chips read as a punched hole when the card is white on a tinted
   ground; give them the band's own tint instead of the fixed ice-2 */
.h27 .h27-act.h27-g--sage .h27-card__ico { background: rgba(122,158,142,.15); }
.h27 .h27-act.h27-g--mist .h27-card__ico,
.h27 .h27-act.h27-g--steel .h27-card__ico { background: rgba(59,134,188,.12); }

/* white cards on a tinted ground need a touch more lift than they did on
   white, where the hairline alone did the work */
.h27 .h27-act[class*="h27-g--"]:not(.h27-g--navy) .h27-card,
.h27 .h27-act[class*="h27-g--"]:not(.h27-g--navy) .h27-lane,
.h27 .h27-act[class*="h27-g--"]:not(.h27-g--navy) .h27-quote {
  box-shadow: 0 1px 2px rgba(13,27,62,.05), 0 14px 34px rgba(13,27,62,.055);
}

/* the sticky act index sat on flat white; over tinted bands it has to be a
   real pane of glass or it reads as a stray white strip. Retunes §6's tokens
   rather than the `background` shorthand, so the free/stuck pair stays a pair
   — setting `background` here would have out-cascaded only one of them. */
.h27 {
  --h27-idx-bg:       rgba(250,252,255,.82);
  --h27-idx-bg-stuck: rgba(250,252,255,.94);
  --h27-idx-hair:     rgba(13,27,62,.10);
}

/* the coverage map is not an act, but it is part of the same sequence */
.h27 .cov-map-section {
  background: var(--h27-mist);
  box-shadow: inset 0 1px 0 rgba(13,27,62,.055), inset 0 -1px 0 rgba(13,27,62,.045);
}

/* ═══════════════════════════════════════════════════════════════════════════
   §21 · LIT BANDS  —  the three sections that used to run dark
   ---------------------------------------------------------------------------
   Client note: the AI interlude (#home-ai-layer), Act 06 (#home-ai) and the
   closing CTA (#home-demo) were all on the void ramp. They now run on the
   light grounds from §15a — no new hues, just the same navy / sky dissolved
   into white that every other light band already uses.

   The sections carry `.h27-act--lit` (or `.h27-close--lit`) INSTEAD of the
   old `--void` tone class, so every `.h27-act--void .thing` rule above simply
   stops matching and the component falls back to its light default: navy
   headline, mute body, coral-on-navy eyebrow, navy outline button, sky link,
   white card. What is left here is the handful of places that hard-coded a
   dark-ground value rather than reading it from the tone class.

   Their atmosphere divs (.h27-atm + .h27-grain — white grid lines and an
   overlay grain, both invisible on a light ground) were swapped in the
   template for the §15b texture layer the other light bands use.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── §21a · THE BANDS ─────────────────────────────────────────────────────
   Grounds come from §15a via `.h27-g--steel` / `.h27-g--mist` on the section,
   which also re-scopes --h27-hair. The sequence now reads:
     … ink → steel → mist(beam) …    (Act 02 → interlude → Act 03)
     … navy → mist → steel …         (integrations → Act 06 → Act 07)
     … paper(rails) → steel          (FAQ → close)
   so no two adjacent bands share a ground. */

/* the ground class paints the real surface; this is the fallback so `--lit`
   is never a transparent band on its own */
.h27 .h27-act--lit { background: var(--h27-ice); color: var(--h27-body); }

/* A light band butting straight onto the dark one above it needs the same
   persimmon edge the interlude used to draw — the tone change alone reads as
   an accident at that seam. */
.h27 .h27-act--ink + .h27-act--lit::before,
.h27 .h27-act--void + .h27-act--lit::before {
  content: ''; position: absolute; inset-inline: var(--h27-gut); top: 0; height: 2px;
  z-index: 1;
  background: rgba(232,89,58,.55);
}

/* ── §21b · AI INTERLUDE (#home-ai-layer) ────────────────────────────────
   The console itself stays a dark product window: it is a screenshot of
   software, and on the steel ground it now reads as one — so only its
   surround needs re-tinting. */

.h27 .h27-act--lit .h27-ail__facts li { border-top-color: var(--h27-hair); }
.h27 .h27-act--lit .h27-ail__facts li:last-child { border-bottom-color: var(--h27-hair); }
.h27 .h27-act--lit .h27-ail__facts b    { color: var(--h27-navy); }
.h27 .h27-act--lit .h27-ail__facts span { color: var(--h27-mute); }

/* the glow was doing the work of separating the panel from a near-black
   ground; on steel it only has to warm the edge, and the shadow goes navy
   because a pure-black drop shadow greys a tinted surface */
.h27 .h27-act--lit .h27-ail__glow { opacity: .46; }
.h27 .h27-act--lit .h27-ail__stage:has(.h27-ail__win[data-phase="think"]) .h27-ail__glow { opacity: .6; }
.h27 .h27-act--lit .h27-ail__stage:has(.h27-ail__win[data-phase="rest"]) .h27-ail__glow { opacity: .38; }
.h27 .h27-act--lit .h27-ail__win {
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12), 0 28px 64px rgba(13,27,62,.26);
}
.h27 .h27-act--lit .h27-ail__win[data-phase="think"] {
  box-shadow: inset 0 0 0 1px rgba(255,171,150,.3), 0 28px 64px rgba(13,27,62,.28);
}

/* ── §21c · ACT 06 · AI & AUTOMATION (#home-ai) ──────────────────────────
   The glass card only exists so a solid white card does not punch a hole in
   a dark act. On mist it is exactly the wrong way round, so the three cards
   return to the page's standard white card language. */

.h27 .h27-act--lit .h27-card--glass {
  background: var(--h27-paper);
  box-shadow: inset 0 0 0 1px var(--h27-hair),
              0 1px 2px rgba(13,27,62,.05), 0 14px 34px rgba(13,27,62,.055);
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.h27 .h27-act--lit .h27-card--glass:hover {
  box-shadow: inset 0 0 0 1px rgba(13,27,62,.16), 0 22px 54px rgba(13,27,62,.12);
}
.h27 .h27-act--lit .h27-card--glass h3 { color: var(--h27-navy); }
.h27 .h27-act--lit .h27-card--glass p  { color: var(--h27-mute); }
.h27 .h27-act--lit .h27-card--glass .h27-card__eyebrow { color: var(--h27-coral); }
.h27 .h27-act--lit .h27-card--glass .h27-card__ico { background: rgba(59,134,188,.12); }
.h27 .h27-act--lit .h27-card--glass .h27-card__ico svg { color: var(--h27-navy); stroke: var(--h27-navy); }
.h27 .h27-act--lit .h27-card--glass .h27-card__arrow { color: var(--h27-navy); }

/* ── §21d · CLOSING CTA (#home-demo) ─────────────────────────────────────
   Not an act, so it cannot pick up a `.h27-g--*` ground — it carries the
   steel ramp itself. Blueprint texture, which bookends Act 01. */

.h27 .h27-close--lit {
  background: var(--h27-steel);
  color: var(--h27-body);
  --h27-hair: #CEDCEC;
  box-shadow: inset 0 1px 0 rgba(13,27,62,.055);
}
/* platinum-theme.css §21b took `--lit` site-wide and draws a persimmon rule on
   the band's top edge to name the seam. The homepage close sits on a photo
   backdrop where that rule would cut across the image, and this band has never
   needed it — the photograph already announces the change. This file loads
   after the theme, so opting out here leaves every other page's rule intact. */
.h27 .h27-close--lit::before { content: none; }
.h27 .h27-close--lit h2 { color: var(--h27-navy); }
.h27 .h27-close--lit p  { color: var(--h27-mute); }
.h27 .h27-close--lit .h27-kicker { color: var(--h27-coral); }
.h27 .h27-close--lit .h27-close__proof { border-top-color: var(--h27-hair); }
.h27 .h27-close--lit .h27-close__proof span { color: var(--h27-mute); }

/* §3 names `.h27-close` explicitly in its dark-ground list; these later rules
   of equal specificity hand the lit band back the light treatment. */
.h27 .h27-close--lit .h27-btn--line {
  color: var(--h27-navy); box-shadow: inset 0 0 0 1.5px rgba(13,27,62,.28);
}
.h27 .h27-close--lit .h27-btn--line::before { background: var(--h27-navy); }
.h27 .h27-close--lit .h27-btn--line:hover   { color: #fff; }
.h27 .h27-close--lit .h27-link { color: var(--h27-sky); }


/* ═══════════════════════════════════════════════════════════════════════════
   §22 · PHOTOGRAPHY  —  the clinic, on the page
   ---------------------------------------------------------------------------
   The page argued entirely in type, rule and mock-up: true to the brand, but
   nothing on it showed a room, a patient or a front desk. This layer brings
   the supplied clinic photography in WITHOUT breaking the surface rhythm §15
   set up, and it does that by refusing to treat a photograph as a free-form
   image. Every frame goes through one of two components:

     .h27-phbg  — a full-bleed BACKDROP behind a band. Sits at z-index 0 with
                  the texture layer, is masked away from the column the type
                  runs in, and is always finished with a scrim so the band's
                  own ground still decides the contrast text is read against.

     .h27-ph    — a visible editorial PLATE inside a light act. Letterboxed,
                  hairlined and corner-bracketed like every other H27 frame,
                  with an optional caption chip.

   THE GRADE is what makes eight unrelated stock frames read as one set. Each
   arrived with its own colour temperature — cyan hex art, a warm treatment
   room, a cold radiology suite. Both components pull saturation back and lay
   the same navy / sky wash over the result, so the photography signs off in
   the brand palette instead of importing three new ones. No photo is ever
   asked to carry text contrast on its own.

   PERFORMANCE: every image is a <picture> with a WebP srcset ladder built by
   scripts/build_chiro_photos.py, cropped on disk to the ratio its slot
   actually renders at. Nothing here is above the fold, so all of it is lazy.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── §22a · BACKDROP ─────────────────────────────────────────────────────
   Same footprint and stacking as .h27-tex, so a band can carry a photo and a
   texture at once and neither one wins. Tuned per band in §22c through the
   four custom properties below rather than by writing a new rule each time. */

.h27 .h27-phbg {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none; overflow: hidden;
  /* defaults are the DARK-band values — light bands override in §22c */
  --h27-phbg-o:    .26;
  --h27-phbg-sat:  .52;
  --h27-phbg-br:   .84;
  --h27-phbg-pos:  50% 50%;
  --h27-phbg-mask: linear-gradient(90deg, transparent 0%, rgba(0,0,0,.34) 42%, #000 84%);
}
.h27 .h27-phbg img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: var(--h27-phbg-pos);
  opacity: var(--h27-phbg-o);
  filter: saturate(var(--h27-phbg-sat)) contrast(1.05) brightness(var(--h27-phbg-br));
  -webkit-mask-image: var(--h27-phbg-mask);
          mask-image: var(--h27-phbg-mask);
}

/* the scrim — the band's own ground, laid back over the photo so the text
   column keeps the contrast it had before there was a photo behind it */
.h27 .h27-phbg::after {
  content: ''; position: absolute; inset: 0;
  background:
    var(--h27-ink);
}

/* on a navy band the scrim has to follow the navy plane, not the deeper ink */
.h27 .h27-act.h27-g--navy .h27-phbg::after {
  background:
    rgba(13,27,62,.74);
}

/* LIGHT bands: the photo is a whisper under a white wash, never a picture.
   Brightness goes up rather than opacity, so the frame stays airy instead of
   turning into a grey smudge on the steel ground. */
.h27 .h27-phbg--lit {
  --h27-phbg-o:   .30;
  --h27-phbg-sat: .40;
  --h27-phbg-br:  1.06;
}
.h27 .h27-phbg--lit::after {
  background:
    rgba(237,243,250,.92);
}

/* the same idea on a PAPER band, where the steel-tinted wash above would read
   as a pale blue rectangle laid over white rather than as depth */
.h27 .h27-phbg--paper {
  --h27-phbg-o:   .26;
  --h27-phbg-sat: .34;
  --h27-phbg-br:  1.10;
}
.h27 .h27-phbg--paper::after {
  background:
    rgba(255,255,255,.90);
}

/* ── §22b · PLATE ────────────────────────────────────────────────────────
   The editorial frame. Ratio is a variable because each act's plate is cut to
   a different letterbox on disk — see the PLAN table in the build script. */

.h27 .h27-ph {
  position: relative; display: block;
  aspect-ratio: var(--h27-ph-ar, 2.4);
  margin: 0 0 clamp(38px, 4.6vw, 68px);
  border-radius: 4px; overflow: hidden;
  background: var(--h27-ice-2);
  box-shadow:
    inset 0 0 0 1px var(--h27-hair),
    0 2px 4px rgba(13,27,62,.05),
    0 26px 64px rgba(13,27,62,.11);
  isolation: isolate;
}
.h27 .h27-ph img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block; object-fit: cover;
  object-position: var(--h27-ph-pos, 50% 50%);
  /* plates are seen at full strength, so the grade is gentle here — just
     enough to take the stock cyan off the highlights */
  filter: saturate(.84) contrast(1.04);
}

/* the navy signature every plate shares */
.h27 .h27-ph::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    rgba(13, 27, 62, 0.29);
}
/* mirrored, for a frame whose subject sits on the left */
.h27 .h27-ph--rtl::after {
  background:
    rgba(13, 27, 62, 0.29);
}

/* corner brackets — the Round-6 frame mark, persimmon, same as the eyebrow */
.h27 .h27-ph__bk { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.h27 .h27-ph__bk::before,
.h27 .h27-ph__bk::after {
  content: ''; position: absolute;
  width: 22px; height: 22px;
  border: 2px solid var(--h27-coral);
}
.h27 .h27-ph__bk::before { top: 14px;   left: 14px;   border-right: 0; border-bottom: 0; }
.h27 .h27-ph__bk::after  { right: 14px; bottom: 14px; border-left: 0;  border-top: 0; }

/* caption chip — what the reader is looking at, in the page's own voice */
.h27 .h27-ph__cap {
  position: absolute; z-index: 3;
  left: clamp(16px, 2.2vw, 30px); bottom: clamp(16px, 2.2vw, 30px);
  display: inline-flex; align-items: center; gap: 11px;
  max-width: min(64ch, calc(100% - 60px));
  padding: 10px 16px; border-radius: 3px;
  background: rgba(10,19,48,.62);
  -webkit-backdrop-filter: blur(12px) saturate(1.3);
          backdrop-filter: blur(12px) saturate(1.3);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.14);
  font-family: 'Inter', sans-serif;
  font-size: 13px; font-weight: 600; letter-spacing: .01em; line-height: 1.35;
  color: #fff;
}
.h27 .h27-ph__cap i {
  flex: none; width: 6px; height: 6px; border-radius: 50%;
  background: var(--h27-coral);
}

/* a plate that closes a block rather than opening one */
.h27 .h27-ph--tail { margin: clamp(38px, 4.6vw, 68px) 0 0; }

/* ── §22c · PER-SLOT TUNING ──────────────────────────────────────────────
   Which photo sits where, and how hard it is pushed. Kept here rather than
   inline on the template for two reasons: the whole photographic rhythm of
   the page can be read — and re-balanced — in one place, and an inline
   `style="--h27-ph-ar:3"` would out-rank the responsive block in §22d, which
   is the one place that MUST be able to stand these letterboxes back up.
   Modifier classes, not `#section` selectors, for the same reason: equal
   specificity means §22d simply wins on source order. */

/* ·· backdrops ···························································· */

/* Act 02 · one operating system (ink). The clinician is on the left of the
   frame and the act's headline is too, so the mask runs the other way and
   the photo surfaces on the right, under the Run / Grow cells. */
.h27 .h27-phbg--os {
  --h27-phbg-o: .30;
  --h27-phbg-pos: 62% 50%;
  --h27-phbg-mask: linear-gradient(115deg, transparent 0%, rgba(0,0,0,.22) 40%, rgba(0,0,0,.9) 88%);
}

/* Integrations (navy). Abstract, so it can run edge to edge — but it is a
   bright cyan frame on a dark plane, which is exactly where a photo starts
   fighting white marquee type. Held down hard and faded at both ends. */
.h27 .h27-phbg--net {
  --h27-phbg-o: .30;
  --h27-phbg-sat: .34;
  --h27-phbg-br: .78;
  --h27-phbg-mask: linear-gradient(180deg, #000 0%, rgba(0,0,0,.34) 46%, rgba(0,0,0,.34) 58%, transparent 100%);
}

/* The Platinum team (ink). Head sits left and the quote rail runs full width
   below it, so the photo takes the top-right quadrant only. */
.h27 .h27-phbg--team {
  --h27-phbg-o: .24;
  --h27-phbg-pos: 58% 34%;
  --h27-phbg-mask: radial-gradient(88% 92% at 88% 12%, #000 0%, rgba(0,0,0,.42) 48%, transparent 82%);
}

/* Closing CTA (light). Centred copy, so the room opens out from behind it
   rather than to one side — the only backdrop on the page not masked off an
   edge. Pairs with `--lit`. */
.h27 .h27-phbg--room {
  --h27-phbg-o: .38;
  --h27-phbg-pos: 50% 58%;
  --h27-phbg-mask: radial-gradient(110% 100% at 50% 100%, #000 0%, rgba(0,0,0,.5) 52%, transparent 88%);
}

/* Act 04 · nine modules (paper). A still-life, not a scene, so it can sit
   almost centred — but the module tab bar is the busiest UI on the page, so
   this is the faintest backdrop in the set. Pairs with `--paper`. */
.h27 .h27-phbg--desk {
  --h27-phbg-o: .36;
  --h27-phbg-br: 1.02;
  --h27-phbg-pos: 50% 46%;
  --h27-phbg-mask: radial-gradient(78% 88% at 82% 8%, #000 0%, rgba(0,0,0,.38) 46%, transparent 80%);
}

/* ·· plates — each ratio matches the crop the build script wrote to disk ·· */

.h27 .h27-ph--booking   { --h27-ph-ar: 3;   }   /* Act 05 · one journey    */
.h27 .h27-ph--consult   { --h27-ph-ar: 2.2; }   /* Act 08 · your practice  */
.h27 .h27-ph--frontdesk { --h27-ph-ar: 2.4; }   /* Act 09 · switching      */
.h27 .h27-ph--clinician { --h27-ph-ar: 2.6; }   /* Act 06 · AI             */
.h27 .h27-ph--outcome   { --h27-ph-ar: 2.4; }   /* Act 07 · proven results */

/* ── §22d · RESPONSIVE ───────────────────────────────────────────────────
   A 3:1 letterbox is a cinematic band at 1320px and a slot at 380px. Below
   the tablet break every plate stands up toward 16:9 and the crop re-centres
   on its subject, which on these frames sits right of centre. */

@media (max-width: 860px) {
  .h27 .h27-ph { --h27-ph-ar: 2.1; }
  .h27 .h27-ph__bk::before,
  .h27 .h27-ph__bk::after { width: 16px; height: 16px; }
}
@media (max-width: 620px) {
  /* 1.9 rather than 16:9: standing a 3:1 all the way up would throw away
     nearly half its width, and these frames do not have half a frame to
     spare. Each one then re-centres on its own subject — object-position is
     the only thing holding the point of the photograph inside the crop. */
  .h27 .h27-ph { --h27-ph-ar: 1.9; border-radius: 3px; }
  .h27 .h27-ph--booking   { --h27-ph-pos: 68% 50%; }  /* the laptop, right of frame  */
  .h27 .h27-ph--consult   { --h27-ph-pos: 50% 50%; }  /* both faces, doctor to patient */
  .h27 .h27-ph--frontdesk { --h27-ph-pos: 62% 50%; }  /* the desk, right of frame    */
  .h27 .h27-ph--clinician { --h27-ph-pos: 56% 50%; }  /* the tablet, just off centre */
  .h27 .h27-ph--outcome   { --h27-ph-pos: 58% 50%; }  /* both faces, patient-side    */
  .h27 .h27-ph__cap {
    left: 12px; right: 12px; bottom: 12px;
    max-width: none; padding: 9px 13px; font-size: 12px;
  }
  /* backdrops lose their directional masks — at this width there is no "other
     side" for the type to sit in, so they simply fade downward */
  .h27 .h27-phbg {
    --h27-phbg-o: .18;
    --h27-phbg-mask: linear-gradient(180deg, #000 0%, rgba(0,0,0,.3) 62%, transparent 100%);
  }
}

/* A decorative photograph is the first thing to go when the reader has asked
   for more contrast. */
@media (prefers-contrast: more) {
  .h27 .h27-phbg { display: none; }
  .h27 .h27-ph::after { background: rgba(13, 27, 62, 0.26); }
}
