/* ==========================================================================
   Health e Match — Landing Portal
   Layout DNA borrowed from mindmarket.com. Palette, type, and radii are
   Health e Match Brand Guide v1.0 and override the reference wherever
   the two collide.
   ========================================================================== */


/* --------------------------------------------------------------------------
   Form and card density

   Lived in forms.css until cards.css needed them too. A token two stylesheets
   depend on belongs in the one they both load.
   -------------------------------------------------------------------------- */
:root {
  --form-gap:   var(--hem-unit);                /* between fields */
  --form-block: calc(var(--hem-unit) * 2);      /* between blocks */
}

/* --------------------------------------------------------------------------
   1. Tokens — verbatim from hem-tokens.css. A raw hex outside this block
      is a bug, not a shortcut.
   -------------------------------------------------------------------------- */
:root {
  /* Core four */
  --hem-teal:            #0D5F72;
  --hem-crimson:         #D9003E;
  --hem-charcoal:        #2E2E2E;
  --hem-canvas:          #E3E3E3;

  /* Teal ramp */
  --hem-teal-50:         #E7EFF1;
  --hem-teal-100:        #C2D7DC;
  --hem-teal-200:        #92B7C0;
  --hem-teal-300:        #568F9C;
  --hem-teal-700:        #0A4756;
  --hem-teal-900:        #063039;

  /* Crimson ramp */
  --hem-crimson-50:      #FBE6EC;
  --hem-crimson-100:     #F6BFCF;
  --hem-crimson-200:     #EE8CA8;
  --hem-crimson-300:     #E44C78;
  --hem-crimson-700:     #A3002E;
  --hem-crimson-900:     #6C001F;

  /* Surfaces */
  --hem-paper:           #FFFFFF;
  --hem-surface:         #EDEDED;
  --hem-surface-raised:  #F7F7F7;
  --hem-line:            #C2CDD0;

  /* Reversed-on-teal */
  --hem-on-teal:         #FFFFFF;
  --hem-on-teal-body:    #E3E3E3;
  --hem-on-teal-label:   #BBD8E0;
  --hem-on-crimson:      #FFFFFF;

  /* Text roles */
  --hem-text:            var(--hem-charcoal);
  --hem-text-heading:    var(--hem-teal);
  --hem-text-muted:      #4E6167;
  --hem-text-match:      var(--hem-crimson);

  /* Semantic — status only, never decorative */
  --hem-success:         #15613B;
  --hem-caution:         #7A4D05;
  --hem-critical:        #A8201A;

  /* Type — Bree is the family. Signika/Karla are the licensed stand-in
     stack; never let a fallback ship as the final artwork. */
  --hem-font-display:    "Bree", Signika, "Trebuchet MS", sans-serif;
  --hem-font-ui:         "Bree", Signika, "Trebuchet MS", sans-serif;
  --hem-font-body:       "Bree", Karla, "Helvetica Neue", Arial, sans-serif;

  --hem-size-display:    3.5rem;
  --hem-size-h1:         2.5rem;
  --hem-size-h2:         1.875rem;
  --hem-size-h3:         1.75rem;   /* 28px */
  --hem-size-body-lg:    1.25rem;   /* 20px */
  --hem-size-body:       1.0625rem; /* 17px */
  --hem-size-caption:    0.875rem;
  /* House standard, set above the guide's documented 15px button size.
     Applies to every button and to the nav links that sit beside them. */
  --hem-size-button:     1.0625rem;
  --hem-size-button-lg:  1.25rem;   /* 20px — reuses body-lg, not a new step */
  --hem-size-label:      0.75rem;

  --hem-lh-display: 1.07;
  --hem-lh-h1:      1.15;
  --hem-lh-h2:      1.2;
  --hem-lh-h3:      1.27;
  --hem-lh-body:    1.62;
  --hem-lh-body-lg: 1.58;
  --hem-lh-caption: 1.5;
  --hem-track-label: 0.14em;

  /* Radii — corners never exceed 14px except pills, which are full */
  --hem-radius-sm:       4px;
  --hem-radius-md:       8px;
  --hem-radius-lg:       14px;
  --hem-radius-pill:     999px;

  --hem-shadow-card:     0 1px 2px rgba(6,48,57,.10), 0 8px 24px -12px rgba(6,48,57,.22);

  /* Space */
  --hem-space-1: 4px;  --hem-space-2: 8px;  --hem-space-3: 12px;
  --hem-space-4: 16px; --hem-space-6: 24px; --hem-space-8: 32px;
  --hem-space-12: 48px;

  --hem-tap-min: 44px;

  /* Motion — the match owns the animation budget */
  --hem-dur-fast:  120ms;
  --hem-dur-heart: 550ms;
  --hem-dur-match: 700ms;
  --hem-ease-glide: cubic-bezier(.22, 1, .36, 1);
  --hem-ease-pop:   cubic-bezier(.34, 1.56, .64, 1);

  /* Page-level layout — reference-derived, brand-constrained */
  --page-max:   1200px;
  --gutter:     clamp(20px, 5vw, 64px);

  /* Vertical rhythm. S1: one unit, and it is the body size — every vertical
     space over 40px on this page is a whole multiple of it. The reference
     runs the same rule off its own 17px body.
     A section contributes HALF the seam, so two adjacent sections realise a
     constant 10u = 170px gap. Overlapping artwork fills that gap where it
     crosses a seam; where nothing crosses, the same 170px reads as clean air
     rather than a hole. This is the one number that used to be four. */
  --hem-unit:   17px;
  --section-gap:       clamp(calc(var(--hem-unit) * 3), 6vw, calc(var(--hem-unit) * 5));
  --section-gap-tight: clamp(calc(var(--hem-unit) * 2), 5vw, calc(var(--hem-unit) * 4));

  /* S3: a heading belongs to the text beneath it, so the space below it is
     small and the large space goes above. One unit under an in-card or step
     heading; two under a display-scale section heading, which needs the extra
     only because it is four times the size. Nothing between a heading and its
     body is ever an arbitrary number again. */
  --hem-lede:    calc(var(--hem-unit) * 2);
  --hem-lede-sm: var(--hem-unit);

  /* Hero display step. The guide's 3.5rem display is a UI-scale ceiling;
     an editorial landing hero extends it. Floor is exactly 3.5rem so the
     mobile rendering lands on the documented scale. */
  --hero-display: clamp(3.5rem, 8.6vw, 7.5rem);
}

/* --------------------------------------------------------------------------
   1b. Desktop type step
   The scale above is set for a phone held at arm's length. On a desktop the
   same rem values sit in a far larger frame and read small - 17px body inside
   a 1200px page is a smaller share of the field of view than 17px inside a
   375px one, so type that felt right on a phone reads undersized here.

   Raised at the token level rather than per component, so body copy, chips,
   captions, labels and buttons all step together and the relationships
   between them survive. Display and h1 are deliberately untouched: they
   already scale with vw and are the one part of the page that was never
   small.
   -------------------------------------------------------------------------- */
@media (min-width: 960px) {
  :root {
    --hem-size-h3:        1.875rem;  /* 28 -> 30 */
    --hem-size-body-lg:   1.375rem;  /* 20 -> 22 */
    --hem-size-body:      1.1875rem; /* 17 -> 19 */
    --hem-size-caption:   0.9375rem; /* 14 -> 15 */
    --hem-size-button:    1.1875rem; /* 19, tracks body */
    --hem-size-button-lg: 1.375rem;  /* 22, tracks body-lg */
    --hem-size-label:     0.8125rem; /* 12 -> 13 */
  }
}

/* --------------------------------------------------------------------------
   2. Reset & base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; color: var(--hem-text); background: var(--hem-canvas); }

body {
  margin: 0;
  /* Soft Studio Gray is the ground. Teal is only ever a block on top of it. */
  background: var(--hem-canvas);
  color: var(--hem-text);
  font-family: var(--hem-font-body);
  font-size: var(--hem-size-body);
  font-weight: 300;
  line-height: var(--hem-lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, svg { display: block; max-width: 100%; }

h1, h2, h3, h4 {
  font-family: var(--hem-font-display);
  font-weight: 700;              /* Bree Extra Bold; 700 is Signika's ceiling */
  color: var(--hem-text-heading); /* Teal above, charcoal below */
  margin: 0;
}

p { margin: 0; }

a { color: inherit; }

::selection { background: var(--hem-teal-100); color: var(--hem-teal-900); }

:focus-visible {
  outline: 3px solid var(--hem-teal);
  outline-offset: 3px;
  border-radius: var(--hem-radius-sm);
}

.u-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: var(--gutter); top: -100px; z-index: 200;
  background: var(--hem-teal); color: var(--hem-on-teal);
  padding: 12px 20px; border-radius: var(--hem-radius-pill);
  font-family: var(--hem-font-ui); font-weight: 700;
  font-size: var(--hem-size-button); text-decoration: none;
  transition: top var(--hem-dur-fast) ease;
}
.skip-link:focus { top: 16px; }

/* --------------------------------------------------------------------------
   3. Type roles
   -------------------------------------------------------------------------- */
.t-display {
  font-family: var(--hem-font-display);
  font-weight: 700;
  font-size: var(--hero-display);
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--hem-text-heading);
}

/* A section heading standing on the ground carries the same authority as the
   hero — this is where the reference gets its confidence, and where we were
   furthest behind it. In-card headings are a separate, smaller step. */
.t-h1 {
  text-wrap: balance;
  font-size: clamp(2.5rem, 6.2vw, 5rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  max-width: 15ch;
}

/* Inside a card the heading has a ~520px column to live in, so it steps down
   to roughly the size the reference uses for the same job. */
.card .t-h1, .portal-card .t-h1 {
  font-size: clamp(1.875rem, 3.4vw, 2.875rem);
  line-height: var(--hem-lh-h1);
  max-width: 18ch;
}
.t-h2 { font-size: clamp(1.625rem, 3.4vw, 1.875rem); line-height: var(--hem-lh-h2); letter-spacing: -0.015em; }
.t-h3 { font-size: var(--hem-size-h3); line-height: var(--hem-lh-h3); letter-spacing: -0.01em; font-weight: 700; }

.t-body-lg {
  font-size: var(--hem-size-body-lg);
  line-height: var(--hem-lh-body-lg);
  color: var(--hem-text);
  max-width: 46ch;
}

.t-body { font-size: var(--hem-size-body); line-height: var(--hem-lh-body); color: var(--hem-text); max-width: 60ch; }

.t-caption { font-size: var(--hem-size-caption); line-height: var(--hem-lh-caption); color: var(--hem-text-muted); }

/* Overline label — the only all-caps in the system */
.t-label {
  font-family: var(--hem-font-ui);
  font-weight: 700;
  font-size: var(--hem-size-label);
  line-height: 1.25;
  letter-spacing: var(--hem-track-label);
  text-transform: uppercase;
  color: var(--hem-text-heading);
  margin: 0 0 var(--hem-lede-sm);
}
.t-label--on-teal { color: var(--hem-on-teal-label); }

/* Match states are the only headings allowed in crimson, and only large. */
.t-match { color: var(--hem-text-match); }

/* --------------------------------------------------------------------------
   4. Layout primitives
   -------------------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-gap); position: relative; }

/* Every in-page link has to clear the sticky navigation. Without this an
   anchor jump puts the target's top edge at y=0 and the nav sits on top of
   it, landing with the section's label tucked behind the bar.

   Five units, not seven. Seven was measured against a 108px nav; the bar is
   76px on a phone and 82px on a desktop now, and its bottom edge sits at
   about 92px including the 16px it floats from the top. The section then adds
   its own padding on top of the offset, so seven units left 70px of empty
   canvas between the bar and the heading - and pushed the cards the jump
   exists to show toward the fold.

   Five units minus 30px, checked on a device rather than derived: five alone
   still landed 30px low. Deliberately off the unit grid - this number is
   measured against the bar's real height plus the section's own padding, and
   rounding it to a unit puts the heading either under the bar or back in the
   dead band. */
main > section,
footer { scroll-margin-top: calc(var(--hem-unit) * 5); }

/* The Join Free title is its own scroll target, so every "Join Free" link
   lands on the words rather than on the section box.

   Anchoring to the section meant the landing was section-padding plus offset,
   and the padding is not a constant - it changed twice while this page was
   being built, and each time the anchor drifted with it. Anchored here the
   sum is one number: the title lands exactly this far down, whatever the
   section around it is doing.

   Six units puts it clear of the bar - which ends at about 84px including the
   8px it floats - and leaves roughly 18px of air, enough to read as a landing
   rather than as a collision.

   This value is the no-JS fallback only. app.js intercepts in-page anchor
   clicks and computes the scroll against where the bar actually is at that
   moment, because no fixed offset can be correct here: the nav shell
   compresses mid-scroll, the browser fixes its destination before that
   happens, and the resulting overshoot depends on the bar's resting height on
   the device. A number tuned on one phone put the title behind the bar on
   another. Six units is simply a sane resting value for the case where the
   script never runs. */
#joinStart { scroll-margin-top: calc(var(--hem-unit) * 6); }

/* A teal block. Never the page ground — canvas always returns after it. */
.block-teal {
  background: var(--hem-teal);
  color: var(--hem-on-teal-body);
}
.block-teal h1, .block-teal h2, .block-teal h3 { color: var(--hem-on-teal); }
.block-teal .t-body-lg, .block-teal .t-body { color: var(--hem-on-teal-body); }
.block-teal .t-caption { color: var(--hem-on-teal-label); }

.split {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(calc(var(--hem-unit) * 2), 5vw, calc(var(--hem-unit) * 4));
  align-items: center;
}
@media (min-width: 900px) {
  .split { grid-template-columns: 1.05fr 0.95fr; }
  .split--reverse > *:first-child { order: 2; }
}

/* Section illustrations run at the full width of their column and are free
   to overflow the section vertically — the art is never cropped, and the
   guide's rule is only that it sits on the canvas, not that it stays inside
   a box. align-items must be explicit: the flex default of `stretch` sets a
   cross-size on the image and squashes it out of its aspect ratio. */
.split [data-art] {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Mechanism 5: the illustration is not contained by the section that owns
     it. It runs past the section's bottom edge into the next one, and
     nothing clips it. */
  position: relative;
  z-index: 0;
}
.split [data-art] img {
  width: 100%;
  height: auto;
  max-width: none;
  max-height: none;
}

/* The practice scene is near square while the patient scene is tall, so at
   equal column width the practice figure reads noticeably smaller. Scaling
   it up matches them optically rather than by the numbers. */
.split [data-art="practice"] img { width: 118%; }

/* --------------------------------------------------------------------------
   5. Buttons
      Match — crimson, once per screen, only for connecting.
      Primary — teal, every other committed action.
      Quiet — outlined teal. Text — dismissals and low-stakes exits.
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  /* A button's label is centred. Without this the flex default packs it to
     the start, which is invisible while the button hugs its text and obvious
     the moment it is stretched - full width on a phone, or side by side in a
     grid. It had been patched in four separate places for exactly that
     reason; it belongs here once. */
  justify-content: center;
  gap: var(--hem-space-2);
  min-height: var(--hem-tap-min);
  padding: 12px 24px;
  border: 2px solid transparent;
  border-radius: var(--hem-radius-pill);
  font-family: var(--hem-font-ui);
  font-weight: 700;
  font-size: var(--hem-size-button);
  line-height: 1.2;
  letter-spacing: 0.005em;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  /* 120ms, brightness and 1px depression only. No bouncing UI outside the match. */
  transition: filter var(--hem-dur-fast) ease,
              transform var(--hem-dur-fast) ease,
              background-color var(--hem-dur-fast) ease,
              color var(--hem-dur-fast) ease;
}
.btn:hover  { filter: brightness(1.08); }
.btn:active { transform: translateY(1px); filter: brightness(0.96); }

.btn--primary { background: var(--hem-teal); color: var(--hem-on-teal); border-color: var(--hem-teal); }
.btn--match   { background: var(--hem-crimson); color: var(--hem-on-crimson); border-color: var(--hem-crimson); }
.btn--quiet   { background: transparent; color: var(--hem-teal); border-color: var(--hem-teal); }
.btn--quiet:hover { background: var(--hem-teal-50); filter: none; }
.btn--text    { background: transparent; color: var(--hem-text-muted); border-color: transparent; padding-inline: 8px; }
.btn--text:hover { color: var(--hem-teal); filter: none; }

/* On a teal block, buttons take the reverse treatment. Crimson never
   sits on teal — a match action inside a teal block gets a plate first. */
.block-teal .btn--primary { background: var(--hem-paper); color: var(--hem-teal); border-color: var(--hem-paper); }
.block-teal .btn--quiet   { color: var(--hem-on-teal); border-color: var(--hem-on-teal-label); }
.block-teal .btn--quiet:hover { background: var(--hem-teal-700); }

.btn--lg { padding: 16px 32px; font-size: var(--hem-size-button-lg); min-height: 56px; }

/* Buttons sitting inside a card are allowed to wrap. Left nowrap they set a
   min-content floor that pushes the whole card wider than its grid track on
   a narrow screen. */
.card .btn, .portal-card .btn { white-space: normal; text-align: center; }


.btn-row { display: flex; flex-wrap: wrap; gap: var(--hem-space-3); align-items: center; }

/* Below the point where the two hero entry points fit on one line they go
   full width instead of sitting half-width and ragged — stacked reads as a
   decision, two short pills stranded left reads as a wrap. */
@media (max-width: 560px) {

}

/* --------------------------------------------------------------------------
   6. Floating pill navigation
      Reference pattern: a single white pill floating on the canvas, not a
      rectangular header. Guide constraint: the wordmark never reverses onto
      teal, so the pill stays white on gray.
   -------------------------------------------------------------------------- */
/* The shell's height NEVER changes. It is sticky, so it occupies flow space,
   and anything that alters its height moves the whole page below it.

   It used to animate its own padding from 16px to 8px on scroll. That is a
   400ms animated layout change, and it ran in exactly the window when a
   smooth anchor scroll was in flight: the browser fixes a scroll destination
   at click time, the page then shortened by 16px underneath it, and every
   in-page link overshot and landed with the target behind the bar. It could
   not be fixed with an offset because the two animations raced, and who won
   depended on the device.

   The pill still tucks up when the page moves - it just does it with a
   transform, which paints without touching layout. Same gesture, no shift,
   and native anchor scrolling works the way it does on every other site. */
.nav-shell {
  position: sticky; top: 0; z-index: 100;
  padding: var(--hem-space-4) var(--gutter);
}
.nav-shell .nav {
  transition: transform var(--hem-dur-match) var(--hem-ease-glide);
}
.nav-shell[data-stuck="true"] .nav { transform: translateY(-8px); }

.nav {
  max-width: var(--page-max);
  margin-inline: auto;
  background: var(--hem-paper);
  border-radius: var(--hem-radius-pill);
  box-shadow: var(--hem-shadow-card);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--hem-space-4);
  padding: 10px 16px;
  min-height: 76px;
}

.nav__brand {
  grid-column: 2;
  justify-self: center;
  display: flex; align-items: center;
  text-decoration: none;
  min-height: var(--hem-tap-min);
}
/* Wordmark minimum on screen is 120px wide. */
.nav__brand img { width: clamp(150px, 17vw, 210px); height: auto; }

.nav__links {
  grid-column: 1;
  justify-self: start;
  display: none;
  align-items: center;
  gap: var(--hem-space-1);
}


.nav__link {
  white-space: nowrap;
  font-family: var(--hem-font-ui);
  font-weight: 500;
  font-size: var(--hem-size-button);
  color: var(--hem-teal);
  text-decoration: none;
  padding: 10px 17px;
  border-radius: var(--hem-radius-pill);
  transition: background-color var(--hem-dur-fast) ease;
}
.nav__link:hover { background: var(--hem-teal-50); }

.nav__actions {
  grid-column: 3;
  justify-self: end;
  display: flex; align-items: center; gap: var(--hem-space-2);
}

/* Below the drawer breakpoint the pill only has room for the wordmark and
   the toggle. The profile CTA is not lost — it leads the drawer. */
@media (max-width: 640px) {
  /* Padding clears the wordmark's required white space at this size. */
  .nav { padding-inline: var(--hem-space-4); gap: var(--hem-space-2); }

  /* The right side is concentric, the left side is not, and they are
     different problems. On the right a pill sits inside a pill: min-height
     centres the 52px button in the 76px bar with 12px above and below, so
     16px beside it made the ring uneven and the button looked pushed toward
     the wordmark. 12px closes it. On the left there is no inner pill to be
     concentric with - just the wordmark, which keeps its 16px of required
     clear space. */
  .nav { padding-right: 12px; }

}

.nav__signin {
  display: none;
  white-space: nowrap;
  font-family: var(--hem-font-ui); font-weight: 500;
  font-size: var(--hem-size-button); color: var(--hem-text-muted);
  text-decoration: none; padding: 10px 14px;
  border-radius: var(--hem-radius-pill);
  transition: color var(--hem-dur-fast) ease;
}
.nav__signin:hover { color: var(--hem-teal); }

/* The header CTA takes the large button's type and height, but not its side
   padding. 32px either side is sized for a pill that fills a card and needs
   to read as a pill; here the button is sized to its own label, so that
   padding was just width - 68px of the 140px was empty space, and the pill
   crowded the wordmark for no reason. Set on the id so it beats .btn--lg
   whatever the source order. */
#navCta { padding-inline: 20px; }


/* Menu control — the profile CTA's pill, a size step down so it reads as
   secondary to it rather than competing with it. Keeps the 44px tap floor. */
.nav__toggle {
  padding: 8px 18px;
  min-height: var(--hem-tap-min);
  font-size: var(--hem-size-button);
  flex: none;
}





/* --------------------------------------------------------------------------
   Nav breakpoints. Two states, not three.

     < 1024  wordmark + Menu. The section links, Sign In and Create Free
             Profile all live in the drawer, so the bar stays the same shape
             on a phone and a tablet.
     >= 1024 the full bar: links, wordmark, Sign In, profile CTA. Menu and the
             drawer are gone.
   -------------------------------------------------------------------------- */
@media (max-width: 1023px) {
  /* Join Free takes the bar; the Menu toggle goes. The one control in the
     header should be the one thing we want somebody to do, and a drawer of
     in-page anchors is not that on a page they can simply scroll. */
  .nav__toggle { display: none; }

  /* With no links in the bar, a centred wordmark leaves the button alone on
     one side and dead air on the other. Anchoring the wordmark left puts the
     two elements at opposite ends and balances the pill. */
  .nav          { grid-template-columns: auto 1fr; }
  .nav__brand   { grid-column: 1; justify-self: start; }
  .nav__actions { grid-column: 2; justify-self: end; }
}

@media (min-width: 1024px) {
  .nav__links  { display: flex; }
  .nav__signin { display: inline-flex; }
  .nav__toggle { display: none; }
  .nav__drawer { display: none !important; }

  /* Concentric, as on a phone: the 62px CTA centres in the 82px bar with 10px
     above and below, so the space beside it has to be 10px too or the pill
     sits off-centre inside the pill. Only from 1024px up, because below that
     the rightmost element is the drawer toggle rather than this button.
     The left stays at 16px - the nav links are text, with no round end to be
     concentric with. */
  .nav { padding-right: 10px; }
}

@media (max-width: 640px) { .nav__toggle { padding: 8px 12px; } }

/* Mobile drawer */
.nav__drawer {
  max-width: var(--page-max);
  margin: var(--hem-space-2) auto 0;
  background: var(--hem-paper);
  border-radius: var(--hem-radius-lg);
  box-shadow: var(--hem-shadow-card);
  padding: var(--hem-space-4);
  display: grid;
  gap: var(--hem-space-1);
}
.nav__drawer[hidden] { display: none; }
.nav__drawer a {
  font-family: var(--hem-font-ui); font-weight: 500;
  font-size: var(--hem-size-body); color: var(--hem-teal);
  text-decoration: none; padding: 12px var(--hem-space-3);
  border-radius: var(--hem-radius-md); min-height: var(--hem-tap-min);
  display: flex; align-items: center;
}
.nav__drawer a:hover { background: var(--hem-teal-50); }
.nav__drawer .btn { margin-top: var(--hem-space-2); }


/* --------------------------------------------------------------------------
   7. Hero
      Reference flow: giant headline on canvas, illustration bleeding from
      the bottom edge into the next section. No card wrapper on the headline.
   -------------------------------------------------------------------------- */
/* Two units, not the full section gap. The nav floats above the hero rather
   than sitting in the flow, so its height is already air between the top of
   the screen and the headline - a section gap on top of that pushed the
   headline a fifth of the way down the first screen on desktop too, not only
   on a phone. */
.hero { padding-top: calc(var(--hem-unit) * 2); padding-bottom: 0; }

/* The nav floats above the hero rather than sitting in the flow, so its own
   height is already air between the top of the screen and the headline. On a
   phone the section gap then adds 51px more, and the headline - the one thing
   that should own the first screen - starts a fifth of the way down it. */
@media (max-width: 640px) {
  .hero { padding-top: var(--hem-unit); }
}

/* Masthead left, lede right, art bleeding full width beneath — the
   reference's headline-then-illustration rhythm, in two beats. */
.hero__top {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(calc(var(--hem-unit) * 2), 4vw, calc(var(--hem-unit) * 3));
  /* The lede column sits on the masthead's baseline, so the entry buttons
     land level with the last line of the headline. */
  align-items: end;
}

/* Stacked, the headline and the lede are one thought in two sizes, so the seam
   between them does not need the full two units it gets when they sit in
   separate columns. Placed after the rule above deliberately: same specificity,
   so source order is what decides it. */
@media (max-width: 640px) {
  .hero__top { gap: var(--hem-space-6); }
}

@media (min-width: 960px) {
  /* The ratio favours the headline, which needs every pixel of its track to
     hold "Healthcare" on the third line. The lede takes what is left; its
     buttons stack full width rather than competing for that space. */
  .hero__top { grid-template-columns: 1.15fr 0.85fr; }

  /* Larger than the shared lede size, because this column is bottom-aligned
     against a 120px headline and the paragraph left ~100px of the track
     empty above it. Filling that with type rather than air gives the lede
     enough presence to answer the headline instead of trailing after it. */
  .hero__sub { font-size: 1.625rem; line-height: 1.45; }

  /* align-items:end levels the two boxes, but the headline's box runs a
     descender's depth below the baseline of 'Healthcare' — a word with no
     descenders, so its ink bottom IS the baseline. Lift the lede by that
     depth and the buttons sit on the letters rather than under them.
     Expressed against the display size so it holds as the clamp scales. */
  .hero__lede { transform: translateY(calc(var(--hero-display) * -0.09)); }
}

/* Measured in em so it tracks the display size rather than the inherited
   body size — the headline should break to three lines, not to one word. */
.hero__display { max-width: 9.2em; text-wrap: balance; }

.hero__sub { margin-bottom: var(--hem-space-8); }
.hero__lede { max-width: 46ch; }

/* The hero's two entry buttons stack full width at every size, not just on a
   phone.

   Side by side they need 475px; the lede column is 434px, so they wrapped -
   and wrapped buttons at two different widths read as two unrelated controls.
   Widening the column was the obvious fix and the wrong one: the headline's
   ink already fills 575px of its 587px track, so any width taken from it puts
   "Healthcare" on a fourth line and costs the hero its shape.

   Full width instead. The two buttons share an edge, the column stays put,
   and the treatment matches the one every other button row already gets on a
   phone. */
.hero__lede .btn-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--hem-space-3);
}
.hero__lede .btn-row .btn { width: 100%; }

/* The audience sections take the same treatment as the hero. Each card holds
   one CTA, and a pill sized to its own label came out 204px in one card and
   211px in the other - close enough to look like a mistake rather than a
   choice. Full width makes them the card's own footer, and matches what the
   join cards and every phone-width button already do. */
#patients .card .btn-row,
#practices .card .btn-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--hem-space-3);
}
#patients .card .btn-row .btn,
#practices .card .btn-row .btn { width: 100%; }


/* Illustration sits directly on the canvas. No frame, no card, no rounded
   clip, no plate — the gray ground touches the artwork on all sides. */
.hero__art {
  margin-top: clamp(calc(var(--hem-unit) * 1), 4vw, calc(var(--hem-unit) * 3));
  /* Bleeds past the text column to the full page width, but never wider —
     the whole two-figure composition has to stay readable, and anything
     past 100% would just push the second figure off the screen. */
  width: 100%;
  margin-inline: auto;
  /* Both figures are absolutely positioned now, so nothing is left in flow to
     give this box a height. The ratio is the original composite's, which keeps
     the reserved space identical to what the single image occupied — no
     layout jump while the two halves decode. */
  aspect-ratio: 2752 / 1305;
}
.hero__art img, .hero__art svg { width: 100%; height: auto; }

/* The hero art finishes inside the section below it, so the seam between the
   two is crossed by the artwork rather than drawn as a line. It passes
   BEHIND any panel or card in that next section — crossing the seam should
   read as depth, not as a collision. */
/* The figures' legs run down over the how-it-works panel rather than being
   clipped behind its top edge. Neither .hero nor #how creates a stacking
   context, so this competes directly with the panel's z-index 3.
   Clicks are unaffected — all [data-art] takes pointer-events: none. */
/* No z-index here on purpose. A positioned element with a z-index creates a
   stacking context, which would trap both figures at one depth and make it
   impossible to put one behind the how-it-works panel and one in front. With
   z-index auto this box creates no context, so each child competes directly
   with the panel's z-index 3. */
.hero__art { position: relative; }
/* This used to pull the panel out to the screen edges on a phone, which was
   reasonable while it was an unshadowed white field - full bleed made it read
   as a change of ground rather than as a card. Now that it carries the same
   shadow and padding as the audience cards it has to keep their width too:
   bled to the edges, the glow is clipped off both sides and the panel sits
   40px wider than the two cards it is sandwiched between. */
.hero__art { position: relative; }

/* --------------------------------------------------------------------------
   8. Organic blocks
      The reference grounds its illustrated sections on large organic blobs.
      Ours are teal blocks, not the page ground, and the canvas returns
      after every one.
   -------------------------------------------------------------------------- */
/* Blobs deliberately run past the section edge, so the section clips them
   rather than letting them widen the page. */
.blob-section { position: relative; isolation: isolate; }

/* More air between the two audience sections on desktop.

   These are the only sections on the page that are structurally identical -
   same split, same floating card, same blob - so at the standard section gap
   they read as one long alternating band rather than as two arguments made
   to two different people. The extra space is what separates them. Below
   900px the split collapses to a single column and the artwork already does
   that separating, so the standard gap is enough. */
@media (min-width: 900px) {
  /* Five units each, which is the page's standard half-seam - so these two
     now sit at the same 10u = 170px as every other pair of sections.

     They used to take eight each, 272px between them, on the argument that
     two structurally identical sections need extra separation or they read as
     one long alternating band. What actually separates them is that each
     carries its own teal blob, on opposite sides, and the blobs meet near the
     seam - the boundary is drawn, not spaced. At 272px the extra air read as
     a hole in the page rather than as a division. Measured after the change:
     the practice scene's top sits 34px below the seam, so it does not cross
     it; only the blob reaches up. */
  #patients { padding-bottom: calc(var(--hem-unit) * 5); }
  #practices { padding-top: calc(var(--hem-unit) * 5); }

  /* Pulled up 30px into the panel above. The How It Works panel lost height
     when its three steps evened out to two lines each, and the section gap
     that was judged against the taller panel now reads long. Taken as a
     negative margin rather than off the section's own padding, so the 5u top
     padding that sets the height of every heading on the page stays intact. */
  #patients { margin-top: -30px; }
}

/* The patient scene must never paint on top of a panel or card. It carries a
   flat sheared edge at its top (see art-source/REGENERATE.md), and a white
   surface passing over that edge is what conceals it — a scene painted on
   TOP of a panel puts the cut on show instead.
   The section therefore keeps z-index auto so the panel's z-index 3 wins.
   Do not raise this: `isolation: isolate` makes each blob-section a single
   stacking context, so it is the section, not the artwork, that decides. */

/* Artwork that sits in front of a panel will otherwise swallow the clicks
   meant for whatever is underneath it — the illustration's transparent
   corners still capture pointer events across the image's full box, which
   silently killed both hero-panel call-to-action buttons. The scenes are
   decorative and carry no interactive content, so they take no pointer
   events at any stacking level. */
[data-art], [data-art] img { pointer-events: none; }

/* The blob is clipped by its own layer rather than by the section, so the
   section can let an illustration run past its edge without the page
   growing sideways. */
.blob-clip {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: -1;
  pointer-events: none;
}

.blob {
  position: absolute;
  background: var(--hem-teal);
  pointer-events: none;
}
/* A blob only ever backs the card side of a split. The illustration keeps
   the Soft Studio Gray ground on all sides — teal is a block, never the
   surface an illustration sits on. */
/* The radii are deliberately far from 50%. Values clustered near half read
   as a circle that has been nudged; pushing the horizontal and vertical pairs
   apart - a wide shoulder against a tight hip - is what makes the silhouette
   read as drawn rather than generated. The two blobs carry different
   distortions so the mirrored sections do not look stamped. */
.blob--right {
  top: 2%; right: -14%;
  width: 56%; height: 94%;
  border-radius: 64% 36% 52% 48% / 38% 62% 38% 62%;
}
.blob--left {
  top: 4%; left: -14%;
  width: 56%; height: 94%;
  border-radius: 38% 62% 44% 56% / 60% 36% 64% 40%;
}
@media (max-width: 899px) { .blob { display: none; } }

/* --------------------------------------------------------------------------
   9. Cards
      14px radius, one soft shadow. Corners never exceed 14px.
   -------------------------------------------------------------------------- */
.card {
  position: relative;
  z-index: 2;
  background: var(--hem-paper);
  border-radius: var(--hem-radius-lg);
  box-shadow: var(--hem-shadow-card);
  padding: clamp(calc(var(--hem-unit) * 2), 3.6vw, calc(var(--hem-unit) * 3));
}
.card > * + * { margin-top: var(--hem-lede-sm); }
.card .btn-row { margin-top: var(--hem-space-6); }

/* A card floating over a teal blob keeps its own white plate, which is
   also what lets a crimson element appear near teal without touching it. */
.card--float { max-width: 520px; }

/* A white panel at section scale rather than three cards floating on the
   ground. White is a surface in this system, not a component colour — it is
   what carries structure once the accent stops doing it. */
.panel {
  position: relative;
  z-index: 3;
  background: var(--hem-paper);
  border-radius: var(--hem-radius-lg);
  /* Same surface treatment as .card - shadow and padding both. The How It
     Works panel and the two audience cards are the same kind of object: a
     white plate sitting on the canvas with artwork crossing behind it. One
     of them having no shadow made it read as the page's background rather
     than as a card, which is the opposite of what its overlap is doing.

     #match overrides the shadow back off further down: that panel is
     deliberately not a surface, so the two white cards inside it have an
     edge to show against. */
  box-shadow: var(--hem-shadow-card);
  padding: clamp(calc(var(--hem-unit) * 2), 3.6vw, calc(var(--hem-unit) * 3));
}

/* --------------------------------------------------------------------------
   Centred section mastheads
   How It Works and Your Move both open the same way — label, display heading,
   lede — and then hand off to a grid. Centring that opening block makes it
   read as a masthead introducing what follows, rather than as a left column
   that happened to stop early.

   The measures are widened for these two only: 15ch/46ch are tuned for text
   sitting against a left edge, and the same values centred inside a 1289px
   panel leave a narrow ribbon stranded in the middle. 18ch and 54ch fill the
   space while staying inside a comfortable line length.
   -------------------------------------------------------------------------- */
/* The hero's artwork already reaches down into this section - its whole job
   is to cross the seam - so the section does not also need a full section-gap
   above it. That gap was being spent twice, once as the art's overlap and
   once as padding underneath it. */
#how { padding-top: calc(var(--hem-unit) * 2); }

#how .panel > .t-label,
#how .panel > .t-h1,
#how .panel > .t-body-lg,
#join .wrap > .t-label,
#join .wrap > .t-h1,
#join .wrap > .t-body-lg {
  text-align: center;
  margin-inline: auto;
}
#how .panel > .t-h1,
#join .wrap > .t-h1 { max-width: 18ch; }
#how .panel > .t-body-lg,
#join .wrap > .t-body-lg { max-width: 54ch; }

/* The two entry buttons close the section, so they centre under the centred
   masthead instead of sitting left under a three-column grid.
   The air above them is 5u — the largest gap the spacing system permits
   inside a block, being half the 10u seam (S4). That separates them clearly
   from the steps without competing with the section boundary itself. */
#how .panel > .btn-row {
  justify-content: center;
  margin-top: calc(var(--hem-unit) * 5);
}
/* 5u is right under a single row of three columns. Stacked, the steps become
   a 496px column and the same gap reads as a hole between the last step and
   the buttons - it was the most obvious break on the phone. */
@media (max-width: 759px) {
  #how .panel > .btn-row { margin-top: calc(var(--hem-unit) * 3); }
}
#how .panel > .t-caption {
  text-align: center;
  margin-top: var(--hem-lede-sm);
}

/* Numbered steps. S4: the largest gap INSIDE a block is exactly half the gap
   above it — 5u against the 10u seam. */
.steps { display: grid; gap: var(--hem-space-8); grid-template-columns: 1fr; margin-top: clamp(calc(var(--hem-unit) * 2), 4.8vw, calc(var(--hem-unit) * 4)); }
/* No column gap on the wide layout. All the separation lives in each step's
   own padding instead, which is what makes the three columns come out
   identical: a gap sits outside the tracks and a divider's padding sits inside
   only the columns that have a divider, so mixing the two gave the first
   column more room than the other two. */
@media (min-width: 760px) { .steps { grid-template-columns: repeat(3, 1fr); column-gap: 0; } }

/* Inside the panel the steps need no surface of their own — a hairline
   between columns is enough to separate them. */
.step {
  display: flex; flex-direction: column; gap: var(--hem-lede-sm);
}
@media (min-width: 760px) {
  /* Every column padded the same, so every column reads the same width. The
     rule then lands on a track edge with one padding of clearance either
     side, which is the symmetry the old gap-plus-padding arrangement could
     not give it. */
  /* Trimmed when the desktop type step took the body copy from 17px to
     19px: at the old padding the wider glyphs pushed steps 2 and 3 onto a
     third line while step 1 stayed at two. The rule still gets clear air
     either side; the columns just stopped paying for it twice. */
  .step { padding-inline: clamp(10px, 1vw, 14px); }
  /* The border is on all three, visible on two. A border only where the rule
     shows would take its 1px out of those columns' content box alone, leaving
     the first column a pixel wider than the other two. */
  .step { border-left: 1px solid transparent; }
  .step + .step { border-left-color: var(--hem-line); }
}
/* The same marker the sign-up form uses for the step somebody is currently on
   - a numeral reversed out of solid teal. Reusing it means the numbering means
   the same thing in both places rather than being two different devices that
   happen to both count.

   aria-hidden in the markup: the numeral is a visual index, and the heading
   after it already says what the step is. A screen reader announcing "one,
   Create a Free Profile" adds nothing to "Create a Free Profile". */
.step__num {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: var(--hem-radius-pill);
  /* The form's resting step marker: outlined, not filled. Filled is what that
     component reserves for the step somebody is actually on, and nothing here
     is "current" - all three are just the sequence. Borrowing the filled state
     would have said something the page does not mean. */
  border: 2px solid var(--hem-teal);
  background: var(--hem-paper);
  color: var(--hem-teal);
  font-family: var(--hem-font-ui);
  font-weight: 700;
  font-size: var(--hem-size-body);
  line-height: 1;
}
/* max-width: none because the column is already the measure. text-wrap:
   pretty because these three sit side by side and a single word alone on the
   last line of one column is visible against its neighbours in a way it never
   would be in running text. Degrades to normal wrapping where unsupported. */
.step .t-body { max-width: none; text-wrap: pretty; }

/* --------------------------------------------------------------------------
   10. Chips
       Chips carry what a dating app would call interests. A shared chip —
       the overlap that caused the match — is the only chip allowed to go
       crimson.
   -------------------------------------------------------------------------- */
.chip-row { display: flex; flex-wrap: wrap; gap: var(--hem-space-2); }

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px;
  border-radius: var(--hem-radius-pill);
  border: 1px solid var(--hem-teal-200);
  background: var(--hem-teal-50);
  color: var(--hem-teal-700);
  font-family: var(--hem-font-ui);
  font-weight: 500;
  font-size: var(--hem-size-caption);
  line-height: 1.25;
  white-space: nowrap;
}

/* Chip padding is a fixed pixel pair, so it does not follow the desktop type
   step up on its own - without this the larger label crowds the pill it sits
   in. Placed after the base rule rather than beside the tokens, because at
   equal specificity the later rule is the one that wins. */
@media (min-width: 960px) {
  .chip { padding: 9px 16px; }
}

/* Tighter side padding on a narrow phone, so two chips still pair on a line.

   The chip row is 252px at 360px wide. "Your Credentials" and "Your
   Procedures" wanted 262px together, so the pair broke and the For Practices
   chips stacked one per line while For Patients - needing 251px, one pixel
   under - still paired. Two structurally identical rows, two different
   shapes, decided by a single pixel.

   The label is what matters at this size, not the pill's generosity, so the
   padding gives way first. 10px buys 8px per chip and puts both rows back on
   the same 2 + 1 shape. */
@media (max-width: 400px) {
  .chip { padding-inline: 10px; }
}
.chip--shared {
  border-color: var(--hem-crimson-200);
  background: var(--hem-crimson-50);
  color: var(--hem-crimson-700);
}
.chip--shared .chip__heart { color: var(--hem-crimson); }
.chip__heart { width: 12px; height: 12px; flex: none; }

/* .chip--badge is retired, not in use. It gave a chip a white ground with
   grey text, which put the For Practices chips in a different visual language
   from the identical row in For Patients directly above them - two ways of
   saying the same kind of thing on one page. Kept defined for any future case
   that genuinely needs a quieter chip; nothing on the site applies it today. */
.chip--badge { background: var(--hem-paper); border-color: var(--hem-line); color: var(--hem-text-muted); }

/* --------------------------------------------------------------------------
   11. Match moment
       Animation is reserved for the match. The stage stays on the gray
       canvas; only the two profile tiles carry teal.
   -------------------------------------------------------------------------- */
/* S2: no per-section padding. The match screen takes the same seam as every
   other section — it used to run 72px while its neighbours ran 184px, which
   is half of why the gap above "Your Move" read as a hole. */
.match { text-align: center; }
/* The heading measure is capped, so it needs centring explicitly inside a
   centred section. */
.match .t-h1, .match .t-body { margin-inline: auto; }

.match__stage {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: clamp(var(--hem-unit), 4vw, calc(var(--hem-unit) * 3));
  max-width: 900px;
  margin: clamp(var(--hem-lede-sm), 4vw, var(--hem-lede)) auto 0;

  /* The cards are the real thing at its real size, and next to an 80px
     display heading a 17.6px name reads as fine print. On a dashboard that
     name sits at 0.63x the section heading; here it was 0.22x.

     So the card is scaled rather than redrawn. Every proportion inside it
     stays exactly as it ships - this is the same card, shown larger, not a
     second card that happens to look similar. */
  --match-scale: 1.22;

  /* A transform does not change the space the grid reserves, so the scaled
     card would overrun the row. This gives back what the scale takes. */
  padding-block: calc(560px * (var(--match-scale) - 1) / 2);
}

/* The tiles ARE cards now - see cards.css. This rule adds the approach and
   nothing else: same 15rem width, same fixed height, same everything. A demo
   card that is wider or shorter than the real one is demonstrating a card the
   product does not have. */
/* The dashboard card is a fixed 35rem because a row of them has to line up
   whatever each one happens to contain. Here there are two, they contain the
   same thing as each other, and there is no row - so the height comes off and
   the card ends where its content does. Everything else about it is still the
   dashboard card, unmodified. */
.match__tile.pcard { height: auto; }

.match__tile {
  width: 15rem;

  /* Each card sits against the heart rather than centred in its own column.
     Centred, the outer half of each 1fr column became dead space and the two
     cards drifted to the edges of the stage - the opposite of two parties
     coming together. */
  justify-self: center;

  /* They approach and stop short. They never overlap — this is an
     introduction, not a merge. */
  transition: transform var(--hem-dur-match) var(--hem-ease-glide);

  /* The same glow the section and portal cards carry. These two sit on the
     grey canvas with a teal blob behind them, and without a shadow the white
     edge dissolves into the blob wherever the two meet. .pcard ships no
     shadow of its own because on a dashboard these sit in a scroll deck where
     every card would cast one; here there are only two, and they are the
     subject. */
  box-shadow: var(--hem-shadow-card);
}
/* No panel behind this section. A white card on a white panel has no edge -
   the shape that makes it read as a card disappears. On a dashboard these sit
   on the grey canvas, so the canvas is what they sit on here too. */
#match .panel {
  background: none;
  box-shadow: none;
  padding-inline: 0;
}

.match__tile--a { justify-self: end;   transform: translateX(-120%) scale(var(--match-scale)); }
.match__tile--b { justify-self: start; transform: translateX(120%)  scale(var(--match-scale)); }
.match.is-playing .match__tile--a { transform: translateX(0) scale(var(--match-scale)); }
.match.is-playing .match__tile--b { transform: translateX(0) scale(var(--match-scale)); }

/* The two portraits are cropped here rather than re-encoded, so the source
   files stay untouched and the framing can be adjusted against the live page.
   Both were shot with the face off the centre of a square frame, which the
   circular mark would otherwise crop badly - Dana low and left, Elena with
   the crown of her head against the mask.

   --z zooms, --x/--y recentre. Percentages are of the image's own box. */
.match__portrait {
  transform: translate(var(--x, 0), var(--y, 0)) scale(var(--z, 1));
}
/* Tuned against the rendered circle, not calculated from the files: the two
   sit side by side, so what matters is that they agree with each other -
   comparable head size, eye lines at roughly the same height. Dana was shot
   loose and low in frame, Elena tight and high, so the corrections run in
   opposite directions. */
.match__portrait--dana  { --z: 1.52; --x: 3.4%;  --y: -10%; }
.match__portrait--elena { --z: 1.42; --x: 0.5%;  --y: 16%; }

.match__avatar {
  width: 64px; height: 64px; margin: 0 auto var(--hem-space-4);
  border-radius: var(--hem-radius-pill);
  background: var(--hem-teal-700);
  border: 2px solid var(--hem-teal-200);
  display: grid; place-items: center;
  font-family: var(--hem-font-ui); font-weight: 700; font-size: var(--hem-size-body-lg);
  color: var(--hem-on-teal);
}
.match__name { font-family: var(--hem-font-ui); font-weight: 700; font-size: clamp(1.25rem, 4.4vw, 1.75rem); line-height: var(--hem-lh-h3); color: var(--hem-on-teal); }
.match__meta { font-size: var(--hem-size-caption); color: var(--hem-on-teal-label); margin-top: var(--hem-space-1); }

/* The Heart e resolves between them — the mark completing the sentence the
   interface just started. It sits on the canvas, never on the teal tiles. */
.match__heart {
  width: clamp(64px, 10vw, 104px);
  height: auto;
  justify-self: center;
}

/* Entrance and heartbeat belong to the mark, not to its wrapper. On the
   wrapper they scaled the blob behind it too, so the backdrop pulsed along
   with the heart - a blob is a ground, and a ground that beats reads as the
   page breathing rather than the mark. */
.match__heart-mark {
  display: block;
  position: relative;
  opacity: 0;
  transform: scale(0.4);
  transition: opacity var(--hem-dur-heart) var(--hem-ease-pop) 450ms,
              transform var(--hem-dur-heart) var(--hem-ease-pop) 450ms;
}
.match.is-playing .match__heart-mark { opacity: 1; transform: scale(1); }
.match.is-playing .match__heart-mark { animation: hem-heartbeat 1400ms ease-in-out 1000ms infinite; }

@keyframes hem-heartbeat {
  0%, 100%  { transform: scale(1); }
  14%       { transform: scale(1.11); }
  28%       { transform: scale(1); }
  42%       { transform: scale(1.07); }
  56%       { transform: scale(1); }
}

/* On a phone there is no room for two tiles to approach each other across
   the screen, so the introduction happens vertically instead. The behaviour
   is identical: they move toward each other and stop short of touching. */
@media (max-width: 700px) {
  .match__stage {
    grid-template-columns: 1fr;
    justify-items: stretch;
    gap: var(--hem-space-4);
  }
  /* justify-items above does not reach these: justify-self on the item wins
     over justify-items on the container, so the desktop rule that pulls the
     two cards toward the heart kept applying once they were stacked - card A
     hard right, card B hard left, staggered by 95px down the middle of a
     phone. Side by side that reads as two cards leaning together; stacked it
     reads as a mistake. */
  .match__tile--a,
  .match__tile--b { justify-self: center; }

  /* The padding above reserves room for scale(--match-scale). The transforms
     below drop the scale entirely on this breakpoint, so that reservation was
     123px of empty stage with nothing standing in it. */
  .match__stage { padding-block: 0; }

  /* Horizontally, the same as the desktop: the patient enters from the left,
     the practice from the right. Stacked they used to slide vertically toward
     each other, which reads as two things settling rather than as two sides
     meeting - and the sides are the whole point of the illustration.

     Safe on a narrow screen because html and body take overflow-x: clip, so
     the offset frames cannot widen the page while they play. */
  .match__tile--a { transform: translateX(-120%); }
  .match__tile--b { transform: translateX(120%); }
  .match.is-playing .match__tile--a,
  .match.is-playing .match__tile--b { transform: translateX(0); }
}

.match__caption {
  margin-top: clamp(calc(var(--hem-unit) * 2), 4vw, calc(var(--hem-unit) * 3));
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 400ms ease 1000ms, transform 400ms ease 1000ms;
}
.match.is-playing .match__caption { opacity: 1; transform: translateY(0); }

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

/* --------------------------------------------------------------------------
   12. Portal — the two front doors
   -------------------------------------------------------------------------- */
.portal__grid {
  display: grid; gap: var(--hem-space-6);
  grid-template-columns: 1fr;
  margin-top: calc(var(--hem-unit) * 3);
}
@media (min-width: 880px) { .portal__grid { grid-template-columns: 1fr 1fr; } }
/* Grid items default to a min-width of auto, which lets long content widen
   the track. Both portal cards must stay inside it. */
.portal__grid > * { min-width: 0; }

.portal-card {
  background: var(--hem-paper);
  border-radius: var(--hem-radius-lg);
  box-shadow: var(--hem-shadow-card);
  padding: clamp(calc(var(--hem-unit) * 2), 3.6vw, calc(var(--hem-unit) * 3));
  display: flex; flex-direction: column;
  border-top: 4px solid var(--hem-teal);
}
.portal-card__title { margin-bottom: var(--hem-lede-sm); }

/* "Match with Practices" has to hold one line inside a 240px card.

   .t-h2 clamps to a 26px floor, which left the longer of the two titles just
   9px of slack at 375px - so it fitted in the test viewport and broke on a
   real 360px phone, and would break anywhere the font renders a shade wider.
   A title that wraps here is not only untidy: the two cards stop matching,
   because the shorter one still fits.

   Lower floor and a vw term, phone widths only. The pair scale together, so
   they keep reading as one control repeated rather than two headings. */
@media (max-width: 430px) {
  .portal-card__title { font-size: clamp(1.25rem, 6.1vw, 1.625rem); }
}
/* The list follows the card title directly, so it takes no top margin of its
   own — the title's 1u bottom margin is the whole gap, matching the
   heading-to-content rule the rest of the page follows (S3). Both values here
   were off-unit before (24px and 32px). */
.portal-card__list {
  list-style: none; padding: 0;
  margin: 0 0 var(--hem-lede);
  display: grid; gap: var(--hem-lede-sm);
}
.portal-card__list li {
  display: flex; gap: var(--hem-space-3);
  font-size: var(--hem-size-body); line-height: var(--hem-lh-body);
}
.portal-card__list svg { width: 18px; height: 18px; flex: none; margin-top: 5px; stroke: var(--hem-teal); }
.portal-card__foot { margin-top: auto; padding-top: var(--hem-lede-sm); }

/* --------------------------------------------------------------------------
   13. Footer — a teal block closing the page, with canvas above it
   -------------------------------------------------------------------------- */
/* The footer is a block, not a seam — its top padding is interior, so it runs
   the deeper 8u the reference uses on its own footer. */
/* The canvas returns after the last section, so the teal block gets the same
   10u seam above it as every other boundary — the join section pays half and
   this margin pays the other half. */
.footer { margin-top: var(--section-gap); }
/* The ceiling was 8 units, which 9.5vw reaches by about 1430px - so on a
   desktop the teal block opened with 136px of empty colour before its first
   word. Four units is enough to seat the content inside the block without the
   footer reading as a room nobody is in. The floor and the vw term are
   unchanged, so narrow widths land exactly where they did. */
.footer { padding-block: clamp(calc(var(--hem-unit) * 3), 9.5vw, calc(var(--hem-unit) * 4)) var(--hem-space-8); }

.footer__top {
  display: grid; gap: var(--hem-space-12);
  grid-template-columns: 1fr;
}
@media (min-width: 820px) { .footer__top { grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--hem-space-8); } }

.footer__lead p { max-width: 20ch; color: var(--hem-on-teal); }

.footer__col h3 {
  font-size: var(--hem-size-label);
  letter-spacing: var(--hem-track-label);
  text-transform: uppercase;
  color: var(--hem-on-teal-label);
  /* Out-specifies the .t-label rule, so it has to carry the unit itself or
     the three footer labels quietly sit a pixel off the rest of the page. */
  margin-bottom: var(--hem-lede-sm);
}
.footer__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--hem-space-3); }
.footer__col a {
  color: var(--hem-on-teal-body);
  text-decoration: none;
  font-size: var(--hem-size-body);
  border-bottom: 1px solid transparent;
  transition: border-color var(--hem-dur-fast) ease;
}
.footer__col a:hover { border-bottom-color: var(--hem-on-teal-label); }

.footer__rule { border: 0; border-top: 1px solid var(--hem-teal-700); color: var(--hem-on-teal-label); background: transparent; margin-block: calc(var(--hem-unit) * 3) var(--hem-lede); }

.footer__legal {
  display: flex; flex-wrap: wrap; gap: var(--hem-space-4) var(--hem-space-8);
  justify-content: space-between; align-items: flex-start;
}
.footer__legal p { font-size: var(--hem-size-body); color: var(--hem-on-teal-label); max-width: 62ch; }

/* --------------------------------------------------------------------------
   14. Disclosure — required, always literal, never shortened for a headline
   -------------------------------------------------------------------------- */
.disclosure {
  /* Three units, not the usual content spacing. This block changes register -
     from the offer to the terms of it - and needs a beat of separation the
     reader can feel before the tone shifts. */
  margin-top: calc(var(--hem-unit) * 3);
  padding: var(--hem-space-4) var(--hem-space-6);
  border-left: 3px solid var(--hem-teal-200);
  background: var(--hem-surface-raised);
  border-radius: 0 var(--hem-radius-md) var(--hem-radius-md) 0;
}
.disclosure p { font-size: var(--hem-size-body); max-width: 60ch; line-height: var(--hem-lh-caption); color: var(--hem-text-muted); max-width: 68ch; }

/* On a wide screen the block sits directly under a row of cards rather than
   after a swipe deck, so it needs a deeper break above it to stop reading as
   a fourth column of the same group - five units rather than three.

   Below it, the reverse: the section's standard bottom padding was sized for
   a section that ends on content. This one ends on fine print, and 85px of
   air under fine print reads as an unfinished page rather than as breathing
   room. */
@media (min-width: 960px) {
  #join .disclosure { margin-top: calc(var(--hem-unit) * 5); }
  #join { padding-bottom: calc(var(--hem-unit) * 2); }
}

/* Every profile shown in marketing is fictional and labeled as an illustration. */
.fiction-note {
  display: block; margin-top: var(--hem-space-4);
  font-size: var(--hem-size-label); letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--hem-text-muted);
}
.block-teal .fiction-note { color: var(--hem-on-teal-label); }

/* --------------------------------------------------------------------------
   15. Scroll reveal — ambient only, 120ms class of motion
   -------------------------------------------------------------------------- */
.reveal { opacity: 0; transform: translateY(16px); }
.reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity 500ms var(--hem-ease-glide), transform 500ms var(--hem-ease-glide);
}

/* The three steps arrive in their own order rather than as one block, so the
   numbering means something in time and not only in labelling. */
.steps .step.reveal.is-in { transition-delay: 0ms; }
.steps .step.reveal.is-in:nth-child(2) { transition-delay: 90ms; }
.steps .step.reveal.is-in:nth-child(3) { transition-delay: 180ms; }

/* --------------------------------------------------------------------------
   15b. Hero entrance
   The hero is the one screen every visitor sees and it used to arrive fully
   formed. The masthead and lede rise in sequence on load; the artwork is left
   to the scroll-reveal system, which already fires immediately above the fold
   and owns that element's transform.
   -------------------------------------------------------------------------- */
@keyframes hem-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.hero__masthead .t-label { animation: hem-rise 620ms var(--hem-ease-glide) both; }
.hero__display           { animation: hem-rise 620ms var(--hem-ease-glide) 110ms both; }
.hero__sub               { animation: hem-rise 620ms var(--hem-ease-glide) 220ms both; }
.hero__lede .btn-row     { animation: hem-rise 620ms var(--hem-ease-glide) 300ms both; }
.hero__lede .t-caption   { animation: hem-rise 620ms var(--hem-ease-glide) 370ms both; }


/* --------------------------------------------------------------------------
   16. Reduced motion — every transition collapses to its end state and the
       heartbeat loop is disabled. The match still reads; it just doesn't move.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  /* The hero entrance stops entirely rather than running fast.
     Everything stays in its resting, fully legible state. */
  .hero__masthead .t-label, .hero__display, .hero__sub,
  .hero__lede .btn-row, .hero__lede .t-caption {
    animation: none !important; opacity: 1; transform: none;
  }
  /* The figures are already in their final position; skip the approach and
     show the composition as it rests. */
  .hero__figure--left, .hero__figure--right {
    animation: none !important; opacity: 1; transform: none;
  }
  .hero__heart img { opacity: 1; transform: scale(1); }
  .steps .step.reveal.is-in,
  .steps .step.reveal.is-in:nth-child(2),
  .steps .step.reveal.is-in:nth-child(3) { transition-delay: 0ms !important; }

  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
  }

  .reveal { opacity: 1; transform: none; }
  .match__tile--a, .match__tile--b { transform: none !important; }
  .match__heart-mark { opacity: 1; transform: scale(1); animation: none !important; }
  .match__caption { opacity: 1; transform: none; }
}


/* --------------------------------------------------------------------------
   17. Seams
   Mechanism 5: an illustration is not contained by the section that owns it.
   Each following section is pulled up over the artwork above it, so the art
   spans the boundary and passes behind that section's white panel or card.
   This is also what tucks the flat bottom edge of the hero art and the flat
   top edge of the patient art out of sight — a cut edge hidden behind a
   surface reads as depth; the same edge sitting in open ground reads as
   damage.
   -------------------------------------------------------------------------- */
/* The hero illustration's lower edge is a flat cut left by the frame of the
   generation that produced it. The white panel below has to finish that edge
   rather than leave it sitting in open ground — a cut hidden under a surface
   reads as depth, the same cut on bare canvas reads as a damaged drawing.
   So the panel is lifted over the artwork explicitly, at EVERY width, and by
   enough that the overlap survives the art reflowing between breakpoints.
   This is measured, not guessed: see the seam check in the README. */
/* S5: the overlap is spent by the ARTWORK, never by the section. The hero
   illustration overflows its own box downward, so the flow — and with it the
   how-it-works section — resumes above the art's visible foot. #how then
   keeps its full 5u top padding and the panel still lands over the flat cut.
   Rhythm and overlap are two numbers now, not one number doing both jobs
   badly. Nothing here is cropped: the art overflows, it is not clipped. */
.hero__art { margin-bottom: clamp(-300px, -20vw, -150px); }

/* The overlap is sized for a wide illustration with height to spare. On a
   phone the art is only 178px tall, so -150px swallowed nearly all of it and
   left a figure clipped mid-limb under the next section. The overlap should
   be a deliberate tuck, not most of the picture. */
@media (max-width: 640px) {
  .hero__art {
    /* Pushes the whole grouping - art and the panel that overlaps it - far
       enough down that the panel's top edge starts below the fold. A card
       edge peeking above the fold reads as the page having a seam right
       where somebody is deciding whether to scroll. */
    margin-top: calc(var(--hem-unit) * 2.5);
    margin-bottom: -40px;
  }
}

/* The How It Works panel rides up into the illustration so the two figures and
   the Heart e stand above its edge rather than the card starting politely
   below them. The section's own top padding was cancelling out the art's
   negative bottom margin, leaving 6px of overlap - effectively none. */
@media (max-width: 640px) {
  #how .panel { margin-top: -60px; }
}

/* --------------------------------------------------------------------------
   The hero heart
   Lifted out of the illustration and replaced with the Heart e mark, so it
   can be sized and animated. It was previously painted into art-hero.png at
   x1294-1457 y467-607; that region is now transparent and the artwork carries
   no crimson of its own.

   Placement is in percentages of the artwork, never pixels, so the mark stays
   locked to the composition as the scene scales: 50% across, 23.3% down —
   the coordinates the painted heart occupied after being raised.

   The rule that once reserved animation for the match moment was struck on
   22 Aug 2026 — see bar.md. Ambient motion is allowed; the match should still
   be the loudest moment on the page, by out-ranking its neighbours rather
   than by being the only one.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   The two figures approach each other
   The composite art-hero.png was split down the 638px transparent column that
   already separated the figures, at exactly 50% of the canvas. Each half is
   1057 of 2752px = 38.41%, so left:0 and right:0 restore the original
   composition to the pixel — the animation ends where the artwork always sat.

   This is the page's own metaphor: two parties moving toward each other and
   the heart resolving between them, which is precisely what the match section
   performs further down. The hero now previews it.
   -------------------------------------------------------------------------- */
.hero__figure {
  position: absolute;
  top: 0;
  width: 38.41%;
}
.hero__figure img { width: 100%; height: auto; }
/* The two figures sit at different depths against the panel below them: the
   patient passes BEHIND it, the clinician crosses in FRONT. The panel is
   z-index 3, so 1 and 6 put one either side of it. */
.hero__figure--left  { left: 0;  z-index: 1; animation: hem-approach-left  950ms var(--hem-ease-glide) 380ms both; }
.hero__figure--right { right: 0; z-index: 6; animation: hem-approach-right 950ms var(--hem-ease-glide) 380ms both; }

@keyframes hem-approach-left {
  from { transform: translateX(-22%); opacity: 0; }
  to   { transform: none;             opacity: 1; }
}
@keyframes hem-approach-right {
  from { transform: translateX(22%); opacity: 0; }
  to   { transform: none;            opacity: 1; }
}

.hero__heart {
  position: absolute;
  left: 50%;
  top: 23.3%;
  width: 9.5%;            /* ~1.6x the painted heart it replaces */
  transform: translate(-50%, -50%);
  /* Above the panel, matching the clinician. It sits well clear of the panel
     in practice, but it must never be the one element that disappears. */
  z-index: 6;
}
.hero__heart img {
  width: 100%;
  height: auto;
  transform-origin: 50% 50%;
  /* The heart resolves once the two figures have met, then beats. Two
     animations share `transform`; the later one in the list wins while it is
     running, so the beat takes over cleanly the moment the arrival finishes.
     The beat reuses the one canonical heartbeat curve defined for the match
     heart, just slower — the hero rests, the match is excited. */
  animation: hem-heart-arrive 520ms var(--hem-ease-pop) 1180ms both,
             hem-heartbeat 2.4s ease-in-out 1700ms infinite;
}

@keyframes hem-heart-arrive {
  from { opacity: 0; transform: scale(.3); }
  to   { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .hero__heart img { animation: none; }
}

/* This was 8u, inflated because the hero artwork used to paint over the panel
   and bury the "How It Works" eyebrow under 85% opaque ink. The patient figure
   now passes BEHIND the panel and only the clinician crosses in front — and
   the clinician is on the far right, nowhere near a left-aligned eyebrow. The
   headroom is no longer buying anything, so it returns to 4u and the section
   sits that much higher. Verified by sampling the artwork's alpha channel
   where the panel's text falls. */
/* The extra headroom existed to keep the heading clear of the illustration
   crossing the panel's top edge. The panel now carries the same padding as the
   audience cards, and the figures were re-tuned above, so the surplus only
   made this card padded differently from the two it sits between. */
#how .panel { padding-top: clamp(calc(var(--hem-unit) * 2), 3.6vw, calc(var(--hem-unit) * 3)); }

/* A section boundary between two stretches of Soft Studio Gray is invisible,
   so artwork spanning it achieves nothing the eye can see. A crossing only
   reads where there is a visible edge — the white panel, or a white card.
   Each illustration therefore overlaps the nearest white surface and passes
   behind it, which is the same move the reference makes with the figure
   whose arm is on the panel and whose legs are on the ground. */
@media (min-width: 900px) {
  /* The patient scene now sits on the right, so it slides LEFT under the
     card — mirrored when the section was flipped horizontally. */
  #patients  [data-art] { margin-left:  clamp(-180px, -10vw, -72px); }
  /* The practice scene now sits on the left, so it slides RIGHT under the
     card — mirrored when the section was flipped horizontally. */
  #practices [data-art] { margin-right: clamp(-180px, -10vw, -72px); }

  /* The second copy is a phone-layout device and must not reach this
     breakpoint. app.js injects it for every width; below 900px it is clipped
     to its right-hand 54% so the clinician's feet land in front of the card.
     No clip rule applies up here, so the whole duplicate was rendering - a
     second physician's head appearing above the card. The desktop layout
     already crosses its seam by other means, so the copy has no job here. */
  #practices [data-art] .art-front { display: none; }

  /* The clinician passes in FRONT of the card here, not behind it. Its right
     edge already reached 76px into the card at z-index 0, so the crossing was
     drawn and then painted over - raising it above the card's z-index 2 is
     what makes the overlap visible rather than theoretical.

     translateX rather than a negative margin for the 30px: the slot's width
     feeds the image's own sizing, so widening the box to move it would have
     scaled the artwork as a side effect of repositioning it. */
  #practices [data-art] {
    z-index: 3;
    /* 32px down as well as left: measured, not judged. The artwork has no
       transparent padding below the figure - ink runs to the last row - so the
       image's bottom edge IS the soles, and it sat exactly 32px above the
       card's bottom. Carried on the transform rather than the margin clamp,
       which also governs how much height the scene contributes to the
       section. */
    transform: translate(-85px, 32px);
  }
}

@media (min-width: 900px) {
  /* Sections no longer move. Each illustration rises on its own to pass
     behind the white surface above it, while the card beside it stays
     anchored to its section's 5u top padding — which is what puts a constant
     10u of air above every heading on the page.
     The overlap is still deliberately generous: it hides a 160px flat shear
     on the patient scene, so a tolerance of a few pixels would be one copy
     edit away from exposing it. The difference is that spending more overlap
     no longer costs the page a single pixel of rhythm. */
  #patients  .card--float,
  #practices .card--float { align-self: start; }

  /* The patient sits at the base of the section rather than floating above
     it. `align-self: end` does the real work — it pins the scene's foot to
     the bottom of the row at every width, instead of relying on a hand-tuned
     pull that drifts as the card's height changes with its copy. The negative
     margin only keeps the scene's margin box shorter than the card so the
     section does not grow taller to accommodate it. */
  #patients  [data-art] {
    align-self: end;
    margin-top: clamp(-200px, -10vw, -60px);

    /* In front of the card, matching the practice scene. Safe despite the
       flat shear along this artwork's top edge: #patients is
       isolation: isolate, so this z-index competes only with its siblings
       inside the section and cannot climb over the #how panel above, which is
       what covers the cut. That coverage is only 4px deep - if the section
       ever moves up relative to the panel, the shear is the first thing that
       shows. */
    z-index: 3;
    transform: translateX(45px) scale(0.961);
    /* Anchored top-right, so shrinking pulls the scene toward that corner
       rather than toward its own centre - the figure keeps its position
       against the panel above and draws away from the card below-left. */
    transform-origin: right top;
  }
  /* Mechanism 5 still has to hold: with the section no longer moving, this
     pull is what keeps the practice scene crossing its seam and passing
     behind the patient card above it, rather than sitting politely inside
     its own section boundaries. */
  /* Was -34vw, which lifted the scene 239px clear of the card and left 320px
     of dead canvas beneath it. That deep pull existed to make this scene
     cross its seam, but the composition costs more than the crossing was
     worth, so the scene now sits level with the card it belongs to. The hero
     and patient illustrations still carry the seam-crossing. */
  #practices [data-art] { margin-top: clamp(-190px, -8vw, -48px); }
}

/* Two columns from the same breakpoint as the portal grid, so the note lines
   up with the cards above it instead of trailing off two thirds across. */
@media (min-width: 880px) {
  .disclosure { display: grid; grid-template-columns: 1fr 1fr; gap: var(--hem-space-8); }
  .disclosure p { margin-bottom: 0 !important; }
}

/* --------------------------------------------------------------------------
   18. Seams, stacked
   On a phone each section leads with its illustration and the card rides up
   over the lower part of it, so the artwork sits behind the card at the TOP
   of the section rather than trailing below it. Same principle as the desktop
   seams — the art passes behind a white surface — just stacked.
   -------------------------------------------------------------------------- */
@media (max-width: 899px) {
  /* Illustration first, card second, whatever the source order. */
  .split [data-art] { order: -1; }
  .split .card      { order: 0; }

  /* The card climbs over the foot of the illustration. */
  .split .card { margin-top: clamp(-200px, -30vw, -90px); }

  /* The illustration leads the section, so the section's top padding sits
     above it doing nothing except pushing the art down — which then forces a
     much deeper negative margin to drag the sliced edge back under the panel
     above, and that surplus is what read as a collision. Dropping the top
     padding lets a far gentler pull do the same job, and the padding that
     used to be spent overlapping is returned as clear space below. */
  /* The padding-top:0 that used to live here was correct while the SECTION
     carried the overlap — the padding did nothing but push the art down and
     force a deeper pull. Now that the artwork carries it, zeroing the padding
     only made the seams uneven: 102px where nothing crossed against 51px
     where the illustration led. The section keeps its padding; the art pulls
     through it. */

  /* S5 holds at every width, not just on desktop: the pull belongs to the
     artwork, never to the section. On a phone the illustration already leads
     its section, so raising the art is the same visual move the section
     margin used to make — without the collateral damage to the seam. The
     extra depth here pays for the top padding the art now pulls through. */
  #patients [data-art] { margin-top: clamp(-280px, -54vw, -150px); }
}

/* The For Patients group sits 100px lower than the pull above leaves it. The
   art and the card move together - the card follows the art in flow, so
   nudging the art carries the whole group and the overlap between them is
   untouched. */
@media (max-width: 899px) {
  /* The art rises 10px from where the 100px nudge left it... */
  #patients [data-art] { margin-top: clamp(-190px, -54vw + 90px, -60px); }

  /* ...and the card drops 50px, so the overlap between them opens up rather
     than the pair moving as a block. The card's own pull is what sets how far
     it climbs over the foot of the illustration. */
  /* +60, not +50: the card follows the art in flow, so the art's 10px rise
     lifts the card with it. Sixty here nets the fifty that was asked for. */
  #patients .card { margin-top: clamp(-140px, -30vw + 60px, -30px); }
}


/* --------------------------------------------------------------------------
   Idle warning

   Fixed to the bottom so it does not push the page around under someone who
   is mid-read, and above everything because it is the one message that has to
   be seen. It disappears on any activity - see session-guard.js.
   -------------------------------------------------------------------------- */
.idle-warning {
  position: fixed;
  left: 50%;
  bottom: var(--hem-space-4);
  transform: translateX(-50%);
  z-index: 9999;

  /* Fixed position shrink-to-fits by default, which wrapped this into a
     narrow column. max-content lets it take the line it wants, capped. */
  width: max-content;
  max-width: min(92vw, 34rem);
  padding: var(--hem-space-3) var(--hem-space-6);
  border-radius: var(--hem-radius-pill);

  background: var(--hem-teal);
  color: var(--hem-on-teal);
  font-family: var(--hem-font-ui);
  font-size: var(--hem-size-caption);
  text-align: center;
  box-shadow: 0 6px 24px rgba(6, 48, 57, 0.28);
}
@media (prefers-reduced-motion: no-preference) {
  .idle-warning { animation: hem-idle-in 200ms var(--hem-ease-glide); }
}
@keyframes hem-idle-in {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}


/* --------------------------------------------------------------------------
   The reassurance line sits under its buttons

   "Free to join. No card required." is a footnote to the buttons above it, not
   to the paragraph above them. Left-aligned under a full-width button it hangs
   off one corner and reads as a stray sentence; centred it belongs to the pair
   it is talking about.

   Everything else in the hero stays left-aligned - centring a headline and a
   lede reads as indecisive at any width, and it would put mobile and desktop
   in different characters rather than different sizes.
   -------------------------------------------------------------------------- */
.hero__lede .t-caption,
#how .panel > .t-caption,
.card .t-caption,
.portal-card__foot { text-align: center; }


/* --------------------------------------------------------------------------
   How It Works, centred

   The heading was already centred and the steps were not, which read as two
   different alignments stacked on each other once the eyebrow and lede came
   out from between them.
   -------------------------------------------------------------------------- */
.step {
  align-items: center;
  text-align: center;
}
.step .t-body { margin-inline: auto; }

/* --------------------------------------------------------------------------
   The hero composition, larger on a phone

   The figures are sized as a percentage of the art box, which was set for a
   wide screen where the two of them plus the heart have room to spread. On a
   375px screen that same percentage leaves them small enough to read as
   decoration rather than as the thing the page is about. Thirty percent up on
   both, so the composition holds its own on the narrow screen it will most
   often be seen on.

   Widths, not transforms: these elements already carry the approach animation
   and the heartbeat on their transform property, and a scale set here would be
   overwritten the moment either one ran.
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .hero__figure { width: 49.9%; }    /* 38.41% x 1.3 */
  .hero__heart  { width: 12.35%; }   /* 9.5%   x 1.3 */

  /* At the larger size the two of them fill the width and meet in the middle,
     which put the heart on top of them instead of in the space between. They
     are pinned to the edges, so pushing each one outward past its edge opens
     that space back up. Part of each figure runs off screen, which is what
     the composition already does at the bottom - it is a crop, not a loss. */
  .hero__figure--left  { left:  -9%; }
  .hero__figure--right { right: -9%; }

  /* Both figures rise; the heart does not. They are pinned to the art box's
     top edge, so a negative top lifts them out of it - the heart keeps its own
     percentage position and stays exactly where it is. */
  .hero__figure { top: -22px; }
}

/* The panel comes down to meet them. This is on top of the -60px pull that
   puts it into the illustration in the first place, so the net overlap is
   what changes, not the pull itself. */
@media (max-width: 640px) {
  #how .panel { margin-top: -45px; }
}

/* --------------------------------------------------------------------------
   The seam between For Patients and For Practices, on a phone

   Both artwork pulls above are scoped to min-width 900px, so below that
   neither scene crosses its section boundary and the two sections meet as a
   flat 102px of empty canvas - 51px closing one, 51px opening the next. That
   band lands between the two audience pitches, which is the worst place on the
   page to look finished.

   Nothing inside either section can close it: the sections grow with their
   content, so the gap is pure padding. The lever is the next section's artwork
   reaching up into it, which is what the desktop rules already do.
   -------------------------------------------------------------------------- */
@media (max-width: 899px) {
  #practices [data-art] { margin-top: -60px; }
}

/* --------------------------------------------------------------------------
   The two front doors swipe on a phone

   Stacked, the practice card sits a full screen below the patient card, so on
   a phone the second door is something you have to scroll past the first to
   discover. Side by side in a swipe they are visibly a pair - which is the
   whole point of the section - and the peek of the second card at the right
   edge is what says there is another one.

   Same mechanism as the dashboard carousels: scroll-snap, no visible
   scrollbar. Native scrolling, so it keeps momentum, keyboard access and
   accessible focus order for free.
   -------------------------------------------------------------------------- */
@media (max-width: 879px) {
  .portal__grid {
    display: grid;
    grid-auto-flow: column;
    /* Wide enough to read as the primary thing on screen, narrow enough that
       the next card's edge shows and says "swipe". */
    grid-auto-columns: 86%;
    grid-template-columns: none;
    gap: var(--hem-space-4);

    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;
    -ms-overflow-style: none;

    /* The row bleeds to the screen edges so a card can sit centred with its
       neighbour peeking, rather than being boxed inside the page gutter. */
    margin-inline: calc(var(--hem-space-4) * -1);
    padding-inline: var(--hem-space-4);
    /* Room for the shadow, which a clipped overflow would otherwise shave. */
    padding-block: var(--hem-space-2);

    /* Tighter under the heading than on desktop. The base rule leaves three
       units, which reads as air below a large desktop h1 but as a disconnect
       below the smaller mobile one - the heading should introduce the cards,
       not stand apart from them. The 8px of shadow padding above counts
       toward the visible gap, so this lands near two units in the end. */
    margin-top: calc(var(--hem-unit) * 1.5);
  }
  .portal__grid::-webkit-scrollbar { display: none; }

  .portal-card {
    scroll-snap-align: center;
    /* Equal height regardless of which card has more copy - a swipe row of
       two different heights reads as a layout fault. */
    height: 100%;
  }
}

/* --------------------------------------------------------------------------
   Nothing scrolls sideways

   Several things here bleed past the viewport on purpose: the hero figures sit
   at a negative offset so the composition opens up around the heart, and the
   swipe row of portal cards is margined out to the screen edges. Intentional
   bleed, but the page still counted it as width and gave the whole document a
   horizontal scrollbar - which on a phone means the layout drifts sideways
   under the thumb.

   `clip`, not `hidden`. hidden creates a scroll container, which would break
   position: sticky on the nav and change how scroll anchoring behaves.
   overflow-x: clip cuts the overflow without any of that, and leaves vertical
   overflow alone - so the figures still run down over the panel below them,
   which is the whole depth effect.
   -------------------------------------------------------------------------- */
html, body { overflow-x: clip; }


/* --------------------------------------------------------------------------
   Every button is full width on a phone

   Not the hero's alone. A row of pills sized to their own labels comes out a
   different width in every section - 198px here, 205px there, 277px in the
   panel - which reads as four different controls rather than the same one
   repeated. Full width also gives the largest possible tap target, which is
   the whole argument on a touch screen.

   The row becomes a single-column grid so stacked buttons share an edge, and
   the buttons stretch to fill it. .btn is inline-flex, which shrink-wraps, so
   the width has to be set explicitly rather than inherited from the row.
   -------------------------------------------------------------------------- */
@media (max-width: 560px) {
  .btn-row {
    display: grid;
    /* minmax(0, 1fr), not 1fr. A bare 1fr is minmax(auto, 1fr), and the auto
       minimum is the item's min-content - so a long label plus 64px of padding
       forced the track wider than the row and the button overflowed its own
       card by 10px instead of filling it. */
    grid-template-columns: minmax(0, 1fr);
    gap: var(--hem-space-3);
  }

  .btn-row .btn {
    width: 100%;
    /* Generous side padding is what makes a pill read as a pill when it is
       sized to its label. Full width, it only squeezes the label toward a
       needless second line. */
    padding-inline: var(--hem-space-4);
  }

  /* One height for every button, whatever its size modifier. .btn--lg exists
     to be the bigger of two sizes on a wide screen; on a phone where they are
     all full width, two heights just reads as an inconsistency. Named
     explicitly rather than relying on source order, since .btn--lg carries the
     same specificity as .btn. */
  .btn,
  .btn--lg,
  .btn--sm {
    min-height: 52px;
    /* One type size as well as one height. .btn--lg and .btn--sm are a size
       ladder for a wide screen; on a phone where every button is full width
       and 52px tall, three different label sizes is just an inconsistency the
       ladder no longer buys anything for. 18px sits between the two rungs -
       bigger than the compact header size, calmer than the display size, and
       comfortably clear of wrapping: the longest label, "Start Practice
       Profile", needs 169px at 18px and the narrowest button carrying it has
       235px of room. */
    font-size: 18px;
    /* Vertical padding, not min-height, was setting the height: .btn--lg's
       16px top and bottom made it 60px while the plain .btn came out 52. With
       the padding removed, align-items: center does the work and every
       single-line button is exactly the min-height. A label that genuinely
       wraps still grows, which is honest - it just should not be the reason
       two buttons differ. */
    padding-block: 0;
  }
}


/* --------------------------------------------------------------------------
   It's a Match, tightened on a phone

   The section is a heading, two cards and a caption - far less content than
   its neighbours - so the standard section padding left it with more air above
   and below than anything else on the page, and the stage carries its own
   padding inside that.
   -------------------------------------------------------------------------- */
/* The same argument holds above phone width, just with more room to spend:
   two units rather than one. Picked up at 641px, exactly where the rule below
   stops, so there is no band left running the full section gap around the
   sparsest section on the page. Each neighbour still contributes its own 85px,
   so the seam reads 119px rather than 170px. */
@media (min-width: 641px) {
  #match { padding-block: calc(var(--hem-unit) * 2); }
}

/* Both seams around It's a Match, trimmed at both ends.

   A seam is paid for by two sections, and this one was costing 170px of ink
   to ink on each side. Cutting only #match could never fix that: its own
   padding was 34px of the 170, while each neighbour contributed 85px of its
   own. So the section drops to one unit and each neighbour gives up two units
   at this seam only - #practices keeps its 8u top, #join keeps its bottom.

   Worth this much: the section is a heading, two cards and a caption. Its
   neighbours are dense. Equal seams around unequal content is what made the
   sparse one look like a gap in the page. */
@media (min-width: 960px) {
  #match { padding-block: var(--hem-unit); }
  #practices { padding-bottom: calc(var(--hem-unit) * 3); }
  #join { padding-top: calc(var(--hem-unit) * 3); }
}

@media (max-width: 640px) {
  #match { padding-block: var(--hem-unit); }
  #match .panel { padding-block: var(--hem-space-2); }
  /* The heading needs room to read as a heading. Tightening the section is
     about the space around the block, not the seam between the title and the
     thing it is titling. */
  .match__stage { margin-top: calc(var(--hem-unit) * 2); }
}

/* --------------------------------------------------------------------------
   The Heart e sits on the bare canvas

   A teal blob used to back the mark here, the way teal backs the cards in the
   audience sections. It was removed: the two cards converging are the subject
   of this section, and a field of teal behind them competed with the one
   thing the section is built to show.

   Worth keeping in mind if anything is ever put behind the mark again: THE
   COUNTER OF THE "e" IS TRANSPARENT, not white. Sampled from the asset - alpha
   0 at the centre of the letter, against #D9003E for the heart body. Whatever
   sits behind the heart shows through the letterform, which is what makes it
   read as knocked out of the grey canvas, and which turned the "e" teal the
   moment the blob went in. That needed a grey shape behind the heart and
   smaller than it; with the blob gone the canvas does the job by itself and
   the extra shape went with it.
   -------------------------------------------------------------------------- */
.match__heart {
  position: relative;
  /* No isolation, deliberately. isolate creates a stacking context, which
     traps the blob below: a negative z-index can only reach the back of its
     own context, so the blob could never get behind the two cards - it sat in
     front of them instead.

     Without it, the nearest stacking context is .match__stage, and the blob
     falls to the back of that: above the section's canvas, behind everything
     drawn in the stage. Which is what a ground is. */
  display: block;
}
.match__heart-img {
  display: block;
  width: 100%;
  height: auto;
  position: relative;
  z-index: 1;
}


/* --------------------------------------------------------------------------
   The join cards' buttons hold one line

   These sit in the swipe row, so the card is 86% of a phone screen and the
   button inside it comes out 220px. At the large button size that leaves
   156px of room for a label needing 187px, and "Start Practice Profile"
   wrapped to two lines - which also made it the only button on the page not
   52px tall.

   Both levers, not just one: the type comes down a step, and the side padding
   with it. Generous padding is what makes a pill read as a pill when it hugs
   its label; on a button sized by its card it only squeezes the label.
   -------------------------------------------------------------------------- */
@media (max-width: 879px) {
  /* The button is as wide as the card's content box, so the card's 34px of
     side padding was the real constraint - it cost 68px of the 288px card
     before the button existed. Trimming the card to 24px gives the button
     240px, and at that width the label fits at full size with 21px to spare.

     Measured rather than guessed: "Start Practice Profile" needs 187px at
     20px type. At the old width it had 156px, which is why it wrapped; at 20px
     with only the button's own padding trimmed it had 188px, a one-pixel fit
     that would break on any device whose font metrics differ slightly. */
  .portal-card { padding-inline: var(--hem-space-6); }

  .portal-card .btn {
    padding-inline: var(--hem-space-4);
    white-space: nowrap;
  }
}

/* --------------------------------------------------------------------------
   The patient illustration crosses in front of its card

   Stacked, the scene ends where the card begins and the overlap between them
   is about 19px. With the art at z-index 0 behind the card at 2, that overlap
   was simply hidden - the illustration looked cropped by a straight edge
   rather than passing behind anything.

   In front, the crossing reads as depth, which is the same move the hero's
   right-hand figure makes over the How It Works panel.

   Safe to raise: [data-art] and its image both take pointer-events: none
   further up, so nothing here can swallow a tap meant for the card.
   -------------------------------------------------------------------------- */
@media (max-width: 899px) {
  #patients  [data-art] { z-index: 3; }

  /* The practice slot takes NO z-index of its own. A z-index here would make
     it a stacking context and trap both copies at one depth - the whole point
     is that its two children sit either side of the card. */
  #practices [data-art] { z-index: auto; }

  /* Behind the card: the stack of profile cards on the left. */
  #practices [data-art] img { position: relative; z-index: 0; }

  /* In front of it: the clinician's feet. Clipped to the right-hand side and
     laid exactly over the copy beneath - same 118% width, and the -9% offset
     is what centring an over-wide image inside its slot already produces. */
  #practices [data-art] .art-front {
    position: absolute;
    /* Matched to the copy beneath by measurement, not by assumption: at this
       breakpoint the image fills its slot at 100%, so the 118% overflow the
       desktop rule applies is not in play here. */
    top: 0;
    left: 0;
    width: 100%;
    height: auto;
    clip-path: inset(0 0 0 46%);
    z-index: 3;
    pointer-events: none;
  }

  /* Dropped 40px from its own pull: enough that the eyebrow and heading stay
     clear of the figure, close enough that the shoes still cross the top edge.
     Around 38px of overlap. Past roughly 90px the overlap closes altogether
     and the split has nothing left to show. */
  #practices .card { margin-top: -77.5px; }
}

/* --------------------------------------------------------------------------
   Scroll-linked cards

   While the convergence is tied to scroll position, the tiles must not carry
   a transition: the transform is being set every frame from the scroll offset,
   and a transition on top of that lags a frame or two behind the finger. It
   turns a direct push-and-pull into something that feels like it is catching
   up with you.

   The class is added by app.js only when it actually takes over, so with
   JavaScript off, or under reduced motion, the transition-based behaviour
   above stays exactly as it was.
   -------------------------------------------------------------------------- */
.match.is-scroll-linked .match__tile { transition: none; }
