@charset "UTF-8";
/**
 * HASfit design system.
 *
 * Every colour and size below was harvested from the approved mockup
 * (design/Main.dc.html and Mobile.dc.html) rather than re-chosen, so what ships
 * matches what the client signed off.
 *
 * SCOPING
 *
 * FitPro and WPBakery both carry opinionated global styles, and the old pages
 * still need them while they exist. So almost nothing here is a bare element
 * selector. Everything lives under .hf-page or a .hf- prefixed class, which
 * means a page built with these components is styled and an untouched legacy
 * page is left exactly as it was. That matters because the ten new pages go
 * live alongside pages that are not being rebuilt.
 *
 * This sheet is enqueued after mo-style-plugins, the parent's last stylesheet,
 * so it wins the cascade on specificity ties. There is no !important anywhere
 * on purpose: if a rule needs one, the scoping is wrong and should be fixed
 * instead.
 *
 * @package HASfit
 */

/* ==========================================================================
   Tokens
   ========================================================================== */

:root {
  /*
   * Brand. Sampled from the logo files, not eyeballed.
   *
   * --hf-red was #E92532 in the mockup. It is nudged one step darker here so
   * that white text on it clears 4.5:1 (4.40 -> 4.54). The shift is 4/255 on
   * red and 1/255 on the other two channels, which is not perceptible side by
   * side, and it is the difference between the primary button passing and
   * failing. See 07-audit/QUALITY-AUDIT.md.
   *
   * Red is a BACKGROUND colour. As text on the paper ground it only reaches
   * 4.12:1, so anything red and textual uses --hf-red-dark, and anything red
   * on a dark ground uses --hf-red-light.
   */
  --hf-red:        #E52431;
  --hf-red-dark:   #B41C26;
  --hf-red-light:  #FF4E5A;  /* red for dark grounds: 5.82:1 on --hf-black */
  --hf-red-pale:   #FFE3E5;

  /* Ink and ground. */
  --hf-ink:        #1C1A19;
  --hf-black:      #131110;
  --hf-paper:      #FAF7F4;
  --hf-white:      #FDFBFA;

  /* Greys, darkest to lightest. */
  --hf-grey-900:   #332F2D;
  --hf-grey-800:   #3A3533;
  --hf-grey-700:   #4A4442;
  --hf-grey-600:   #5A5350;
  --hf-grey-500:   #6B6462;
  --hf-grey-400:   #8C8480;
  --hf-grey-300:   #9C9491;

  /* Surfaces and borders on the light ground. */
  --hf-line:       #E5DDD7;
  --hf-line-warm:  #C9BFB8;
  --hf-line-input: #948780;  /* 3.48:1 on white, WCAG 1.4.11 for field edges */
  --hf-tint:       #F0EAE5;
  --hf-tint-soft:  #F4F0EC;

  /* On the dark ground. */
  --hf-on-dark:        #F2EEEB;
  --hf-on-dark-muted:  #C9C2BF;
  --hf-on-dark-soft:   #E6E1DE;

  /* Type. */
  --hf-display: 'Oswald', 'Arial Narrow', Impact, sans-serif;
  --hf-body:    'Source Sans 3', 'Segoe UI', system-ui, -apple-system, sans-serif;

  /*
   * Fluid type. The mockup is drawn at 1440px desktop and 390px mobile, and
   * these clamps interpolate between the two artboards rather than snapping at
   * a breakpoint. Minimums are the mobile artboard's actual sizes.
   */
  --hf-t-hero:  clamp(2.125rem, 1.30rem + 3.38vw, 4rem);     /* 34 -> 64 */
  --hf-t-h1:    clamp(1.875rem, 1.44rem + 1.80vw, 2.75rem);  /* 30 -> 44 */
  --hf-t-h2:    clamp(1.625rem, 1.32rem + 1.26vw, 2.375rem); /* 26 -> 38 */
  --hf-t-h3:    clamp(1.1875rem, 1.06rem + 0.53vw, 1.4375rem);/* 19 -> 23 */
  --hf-t-lead:  clamp(1.0625rem, 0.97rem + 0.39vw, 1.25rem); /* 17 -> 20 */
  --hf-t-body:  clamp(1rem, 0.96rem + 0.17vw, 1.0625rem);    /* 16 -> 17 */
  --hf-t-small: 0.9375rem;                                    /* 15 */
  --hf-t-micro: 0.84375rem;                                   /* 13.5 */

  /* Rhythm. Section padding collapses hard on phones. */
  --hf-gutter:     clamp(1rem, 0.35rem + 2.78vw, 4rem);      /* 16 -> 64 */
  --hf-section-y:  clamp(2.75rem, 1.80rem + 4.05vw, 4.75rem);/* 44 -> 76 */
  --hf-gap:        clamp(1.25rem, 0.90rem + 1.50vw, 2rem);

  --hf-radius: 3px;
  --hf-max:    1440px;
}

/* ==========================================================================
   Base, scoped to a HASfit page
   ========================================================================== */

.hf-page {
  background: var(--hf-paper);
  color: var(--hf-ink);
  font-family: var(--hf-body);
  font-size: var(--hf-t-body);
  line-height: 1.6;
  /* The old pages can scroll sideways on a phone. These will not. */
  overflow-x: hidden;
}

.hf-page h1,
.hf-page h2,
.hf-page h3,
.hf-page h4,
.hf-page .hf-disp {
  font-family: var(--hf-display);
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.06;
  margin: 0;
  text-transform: uppercase;
}

.hf-page h1 { font-size: var(--hf-t-h1); }
.hf-page h2 { font-size: var(--hf-t-h2); }
.hf-page h3 { font-size: var(--hf-t-h3); }

/*
 * :where() ON PURPOSE. DO NOT REMOVE THE WRAPPER.
 *
 * `.hf-page p` is (0,1,1) and every component class that wants its own spacing
 * -- .hf-hero__sub, .hf-eyebrow, .hf-lead, .hf-hero__note -- is (0,1,0), so the
 * blanket reset silently won and their margins never applied. The hero headline
 * and the paragraph beneath it were touching, with a margin-top set that did
 * nothing.
 *
 * :where() contributes no specificity, so this stays a default that any
 * component can override, which is what a reset is supposed to be. Same family
 * of fault as the buttons losing their colour to `.hf-page a`.
 */
.hf-page :where(p) { margin: 0 0 1em; }
.hf-page :where(p:last-child) { margin-bottom: 0; }

/*
 * Prose rhythm.
 *
 * Headings are margin:0 because the templates position them, which is right
 * there and wrong inside the_content(), where a client's H2 sat directly on
 * the paragraph beneath it. These give editable copy its spacing back.
 *
 * Both sides wrapped in :where() so the whole selector stays (0,1,0) and any
 * template rule still outranks it.
 */
.hf-page :where(h2, h3, h4) + :where(p) { margin-top: 0.55em; }
.hf-page :where(p, ul, ol) + :where(h2) { margin-top: 1.7em; }
.hf-page :where(p, ul, ol) + :where(h3, h4) { margin-top: 1.4em; }

.hf-page a { color: var(--hf-red-dark); text-decoration: none; }
.hf-page a:hover,
.hf-page a:focus-visible { color: var(--hf-red-dark); text-decoration: underline; }

/*
 * Links in prose carry an underline, not just a colour.
 *
 * red-dark against ink is 2.59:1. WCAG 1.4.1 wants 3:1 before colour alone may
 * be the thing that marks a link, so on this palette colour alone is not
 * enough and the underline is doing real work rather than decorating.
 *
 * Scoped to p and li so navigation, cards and buttons are unaffected.
 */
.hf-page p a,
.hf-page li a { text-decoration: underline; }

/*
 * Chrome is exempt. 1.4.1 is about links inside a block of text, where nothing
 * but colour would mark them. Navigation, the footer menu and buttons are
 * already identifiable by position and shape, and the footer menu is a <ul>,
 * so without this it would underline every item.
 */
.hf-page .hf-nav a,
.hf-page .hf-utility a,
.hf-page .hf-footer a,
.hf-page .hf-btn { text-decoration: none; }

.hf-page img { max-width: 100%; height: auto; display: block; }

/* Focus ring. FitPro removes outlines in places; this puts a visible one back. */
.hf-page a:focus-visible,
.hf-page button:focus-visible,
.hf-page input:focus-visible,
.hf-page select:focus-visible,
.hf-page textarea:focus-visible {
  outline: 3px solid var(--hf-red);
  outline-offset: 2px;
}

/* Screen-reader-only, for the skip link and form labels that are visually implicit. */
.hf-sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   Layout
   ========================================================================== */

.hf-section {
  padding: var(--hf-section-y) var(--hf-gutter);
}

.hf-section--dark {
  background: var(--hf-black);
  color: var(--hf-on-dark);
}

.hf-section--dark h1,
.hf-section--dark h2,
.hf-section--dark h3 { color: #fff; }

.hf-section--tint { background: var(--hf-tint-soft); }

.hf-wrap {
  max-width: var(--hf-max);
  margin-inline: auto;
}

/*
 * Two-column split. Stacks below 860px, which is where the mockup's paired
 * image-and-text sections stop having room for both.
 */
.hf-split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.75rem, 1.0rem + 3.2vw, 4rem);
  align-items: center;
}

@media (max-width: 860px) {
  .hf-split { grid-template-columns: minmax(0, 1fr); }
}

/* Auto-fitting card grid. One column on a phone, no media query needed. */
.hf-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: var(--hf-gap);
}

.hf-grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }
.hf-grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr)); }

/*
 * Results: three cards, so the row is three or one and never two.
 *
 * auto-fit gives three above about 1050px, then two with the third stranded on
 * a row of its own between roughly 740 and 1050. A stranded before-and-after
 * reads as an afterthought, and these are the strongest proof on the page.
 *
 * Below the breakpoint each card is capped and centred, because a single card
 * spanning 900px of a tablet would put one square photograph most of a screen
 * tall.
 */
.hf-grid--results { grid-template-columns: 1fr; }

@media (max-width: 999px) {
  .hf-grid--results > * {
    width: 100%;
    max-width: 540px;
    margin-inline: auto;
  }
}

@media (min-width: 1000px) {
  .hf-grid--results { grid-template-columns: repeat(3, 1fr); }
}

/*
 * Faces two-up on a phone.
 *
 * auto-fit at a 210px minimum drops to a single column below about 460px, so
 * the homepage's six trainers became six full-width portraits and roughly
 * 2400px of scrolling between the InBody section and the locations. On the
 * trainers page, nineteen of them.
 *
 * A headshot is still perfectly legible at 165px, and the point of the grid is
 * to let somebody scan faces rather than study one.
 */
@media (max-width: 639px) {
  .hf-grid--4 { grid-template-columns: repeat(2, 1fr); }
}

/*
 * Services: four cards, so auto-fit is the wrong tool.
 *
 * With minmax(320px) the row holds four only when the wrap is at its full
 * 1440 cap, which needs a viewport wider than about 1570px. Between roughly
 * 1130 and 1570 it fits three and the fourth orphans onto a row of its own.
 * That band contains almost every laptop, 1440 and 1536 included, so the
 * common case was the broken one and the wide case was the one being tested.
 *
 * Explicit counts instead: four, two, one. A four-item row never leaves one
 * card stranded.
 */
.hf-grid--services { grid-template-columns: 1fr; }

@media (min-width: 640px) {
  .hf-grid--services { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1200px) {
  .hf-grid--services { grid-template-columns: repeat(4, 1fr); }
}

/* ==========================================================================
   Type helpers
   ========================================================================== */

/* The mockup draws a short rule before the hero eyebrow. */
.hf-eyebrow--rule { display: flex; align-items: center; gap: 12px; }
.hf-eyebrow--rule::before {
  content: "";
  width: 28px;
  height: 2px;
  background: currentColor;
  flex: 0 0 auto;
}

.hf-eyebrow {
  font-size: var(--hf-t-micro);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--hf-red-dark);
  margin-bottom: 0.7em;
}

/*
 * Dark grounds. The hero counts: its scrim runs to rgba(19,17,16,.88) behind
 * the text block, so anything sitting there is effectively on --hf-black.
 * red-dark on that ground is 2.81:1 and the eyebrow was illegible.
 */
.hf-section--dark .hf-eyebrow { color: var(--hf-red-light); }

.hf-lead {
  font-size: var(--hf-t-lead);
  line-height: 1.6;
  color: var(--hf-grey-500);
}

.hf-section--dark .hf-lead { color: var(--hf-on-dark-muted); }

.hf-muted { color: var(--hf-grey-500); }
.hf-small { font-size: var(--hf-t-small); }

.hf-section > .hf-wrap > h2 + .hf-lead,
.hf-section > h2 + .hf-lead { margin-top: 0.5em; }

/* ==========================================================================
   Buttons
   ========================================================================== */

.hf-btn {
  display: inline-block;
  font-family: var(--hf-body);
  font-size: var(--hf-t-body);
  font-weight: 700;
  line-height: 1.2;
  padding: 15px 28px;
  border: 2px solid transparent;
  border-radius: var(--hf-radius);
  text-align: center;
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
  /* Comfortably above the 44px minimum tap target. */
  min-height: 52px;
}

/*
 * SCOPED UNDER .hf-page ON PURPOSE. DO NOT UNSCOPE.
 *
 * .hf-page sits on <body>, so `.hf-page a` (0,1,1) outranks a bare
 * `.hf-btn--primary` (0,1,0) however far down the file it appears. Every
 * primary button on the site was therefore rendering the link colour, which
 * after the contrast pass meant --hf-red-dark text on a --hf-red background.
 * Near enough invisible, on the one element the whole page exists to get
 * clicked.
 *
 * The token audit did not catch it because it measured the pairs the rules
 * intended rather than the pairs the cascade produced. Found in the full-page
 * screenshots of 18 Sep. Any colour rule for a control needs the .hf-page
 * prefix to survive.
 */
.hf-page .hf-btn--primary {
  background: var(--hf-red);
  border-color: var(--hf-red);
  color: #fff;
}

.hf-page .hf-btn--primary:hover,
.hf-page .hf-btn--primary:focus-visible {
  background: var(--hf-red-dark);
  border-color: var(--hf-red-dark);
  color: #fff;
  text-decoration: none;
}

.hf-page .hf-btn--ghost {
  background: transparent;
  border-color: currentColor;
  color: var(--hf-ink);
}

/* Ghost on any dark ground, hero included. */
.hf-page .hf-section--dark .hf-btn--ghost { color: #fff; }

.hf-page .hf-btn--ghost:hover,
.hf-page .hf-btn--ghost:focus-visible {
  background: var(--hf-ink);
  border-color: var(--hf-ink);
  color: #fff;
  text-decoration: none;
}

.hf-page .hf-section--dark .hf-btn--ghost:hover,
.hf-page .hf-section--dark .hf-btn--ghost:focus-visible {
  background: #fff;
  border-color: #fff;
  color: var(--hf-black);
}

.hf-btn--block { display: block; width: 100%; }

/* ==========================================================================
   Utility bar and header
   ========================================================================== */

.hf-utility {
  background: var(--hf-black);
  color: var(--hf-on-dark-muted);
  font-size: var(--hf-t-small);
  padding: 11px var(--hf-gutter);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}

.hf-utility a { color: inherit; }
.hf-utility strong { color: #fff; }

.hf-utility__locations { display: flex; gap: 28px; align-items: center; flex-wrap: wrap; }
.hf-utility__item { display: flex; gap: 7px; align-items: center; }

/* The hours note is the first thing to go when space runs out. */
@media (max-width: 760px) {
  .hf-utility__hours { display: none; }
}

/*
 * Three tracks: logo, navigation, actions. The navigation sits in the middle
 * track rather than being pushed against the call to action, which is what the
 * mockup shows and what left a conspicuous hole beside the logo before.
 */
.hf-header {
  background: var(--hf-paper);
  border-bottom: 1px solid var(--hf-line);
  padding: 16px var(--hf-gutter);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--hf-gap);
}

.hf-header__actions {
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 0.3rem + 1.2vw, 1.5rem);
}

.hf-header__call { line-height: 1.15; text-align: right; }

.hf-header__call-label {
  display: block;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--hf-grey-500);
}

.hf-page .hf-header__call a {
  font-family: var(--hf-display);
  font-size: 1.375rem;
  color: var(--hf-ink);
  white-space: nowrap;
  text-decoration: none;
}

.hf-page .hf-header__call a:hover { color: var(--hf-red-dark); }

/* The phone block is the first thing to go when the row runs out of room. */
@media (max-width: 1100px) {
  .hf-header__call { display: none; }
}

/* Icon buttons: phone and menu. Narrow screens only. */
.hf-iconbtn {
  display: none;
  width: 46px;
  height: 46px;
  border: 0;
  border-radius: var(--hf-radius);
  cursor: pointer;
  place-items: center;
  background: var(--hf-red);
  color: #fff;
}

.hf-iconbtn--call { color: #fff; }
.hf-page .hf-iconbtn--call { color: #fff; }

.hf-navtoggle {
  background: transparent;
  color: var(--hf-ink);
  border: 1px solid var(--hf-line-warm);
}

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

.hf-header__logo img { height: 50px; width: auto; }

@media (max-width: 600px) {
  .hf-header__logo img { height: 38px; }
}

.hf-nav {
  display: flex;
  gap: clamp(0.85rem, 0.3rem + 1.5vw, 1.75rem);
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  font-size: var(--hf-t-small);
  font-weight: 600;
}

.hf-nav a { color: var(--hf-ink); }
.hf-nav a[aria-current="page"] { color: var(--hf-red-dark); }

/*
 * THE MOBILE HEADER
 *
 * Below 960px the header becomes logo, phone, menu, and the links move into a
 * panel. Six links wrapping onto a second row is not navigation.
 *
 * The panel is hidden with `display: none` keyed on the toggle's aria-expanded,
 * so the attribute a screen reader reads and the state a sighted user sees are
 * the same value and cannot drift. If the script fails to load the panel simply
 * never opens; the links are still in the document and still reachable, which
 * is why the phone button sits outside it.
 */
@media (max-width: 960px) {
  .hf-header {
    grid-template-columns: 1fr auto;
    row-gap: 0;
    position: relative;
  }

  .hf-iconbtn { display: grid; }

  .hf-header__call,
  .hf-header__actions .hf-btn { display: none; }

  .hf-nav {
    grid-column: 1 / -1;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin-top: 14px;
    border-top: 1px solid var(--hf-line);
    font-size: var(--hf-t-body);
  }

  .hf-header:has(.hf-navtoggle[aria-expanded="true"]) .hf-nav { display: flex; }

  .hf-nav a {
    padding: 14px 2px;
    border-bottom: 1px solid var(--hf-line);
  }

  .hf-nav a[aria-current="page"] { color: var(--hf-red-dark); }
}

/*
 * Fallback for browsers without :has(). The script also sets this class on
 * <html>, so the panel opens either way.
 */
@media (max-width: 960px) {
  .hf-nav-open .hf-nav { display: flex; }
}

/* ==========================================================================
   Hero
   ========================================================================== */

/*
 * Split hero, as the approved mockup has it: copy on the paper ground at the
 * left, photograph at the right with an offset tint block behind it and a
 * review card lapping its lower corner.
 *
 * GRID, NOT AN OVERLAY, AND DELIBERATELY SO.
 *
 * The first build laid the copy over a full-bleed photograph with
 * `position: absolute; bottom: 0`. That made the section's height the
 * photograph's height. On the four service pages, where the hero photo is
 * still a placeholder only 280px tall, the copy was taller than the section
 * containing it and rendered upward through the header and across the logo.
 *
 * Sizing the section from its own content removes the whole class of fault,
 * and it is also what the mockup asked for in the first place.
 */
.hf-hero {
  background: var(--hf-paper);
  color: var(--hf-ink);
  padding: clamp(2.25rem, 1.4rem + 3.6vw, 4.5rem) var(--hf-gutter);
}

.hf-hero__inner {
  max-width: var(--hf-max);
  margin-inline: auto;
  display: grid;
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  align-items: center;
}

/*
 * ORDER ON A PHONE IS NOT THE DESKTOP ORDER.
 *
 * The mobile artboard runs eyebrow, headline, PHOTOGRAPH, paragraph, button.
 * The picture sits directly under the headline rather than after all of the
 * copy, which is why the hero is split into three parts rather than two: on a
 * narrow screen they stack in source order and land exactly as drawn.
 *
 * On desktop the grid puts head and body in the left track and the figure in
 * the right one, spanning both rows, which is the desktop artboard.
 */
@media (min-width: 900px) {
  .hf-hero__inner { grid-template-columns: 1fr 1.04fr; }
  .hf-hero__figure { align-self: center; }
}

/*
 * A hero with nothing to show runs as one column of copy, centred, rather than
 * as a two-column layout with an empty half. Used by the in-home page until a
 * real photograph of an in-home session exists.
 */
@media (min-width: 900px) {
  .hf-hero__inner--solo {
    grid-template-columns: minmax(0, 68ch);
    justify-content: center;
    text-align: center;
  }

  .hf-hero__inner--solo .hf-hero__sub { max-width: none; }
  .hf-hero__inner--solo .hf-hero__cta { justify-content: center; }
}

/*
 * Below the split the three parts stack in source order, which is head, body,
 * figure. The artboard wants head, FIGURE, body, so the picture is pulled up
 * between them. Reordering here rather than in the markup keeps the reading
 * order sensible for a screen reader on desktop, where the copy is one column.
 */
/*
 * .hf-hero__copy holds the headline and the paragraph so that on desktop they
 * are one block in the left column. Without it the figure spanned two grid
 * rows, and because a spanning item sizes the tracks it crosses, the extra
 * height was handed to the gap between the headline and the paragraph. They
 * drifted a hundred pixels apart.
 *
 * On a phone the wrapper becomes display:contents, which dissolves it and
 * promotes head and body to grid items in their own right, so the photograph
 * can be ordered between them the way the artboard draws it.
 */
@media (max-width: 899px) {
  .hf-hero__copy   { display: contents; }
  .hf-hero__head   { order: 1; }
  .hf-hero__figure { order: 2; }
  .hf-hero__body   { order: 3; }
  .hf-hero__inner  { gap: 1.75rem; }

  /*
   * The review card is desktop only, as the artboard has it. On a phone it
   * sits between the photograph and the copy and pushes the button a full
   * card's height further down, and the proof bar immediately below already
   * carries the rating. Not a loss worth the scroll.
   */
  .hf-hero__quote { display: none; }
}

/* The paragraph carries the gap under the headline once they are separated. */
.hf-hero__body { margin-top: 0.85em; }

@media (min-width: 900px) {
  .hf-hero__body { margin-top: 1rem; }
}

.hf-hero h1 {
  font-size: var(--hf-t-hero);
  color: var(--hf-ink);
}

.hf-hero__sub {
  font-size: var(--hf-t-lead);
  color: var(--hf-grey-600);
  max-width: 48ch;
}

.hf-hero__cta {
  margin-top: 1.75em;
  display: flex;
  gap: 1.5rem;
  align-items: center;
  flex-wrap: wrap;
}

.hf-hero__note {
  margin: 0;
  font-size: var(--hf-t-small);
  color: var(--hf-grey-500);
  line-height: 1.45;
}

/* Tint block behind, photograph, review card over the corner. */
.hf-hero__figure { position: relative; }

.hf-hero__figure::before {
  content: "";
  position: absolute;
  z-index: 0;
  left: -18px;
  top: -18px;
  width: 46%;
  aspect-ratio: 1 / 1;
  background: var(--hf-red-pale);
}

.hf-hero__media { position: relative; z-index: 1; }

.hf-hero__media img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: 50% 35%;
  display: block;
  /*
   * Capped so the hero cannot fill a laptop screen on its own. At 4/5 in a
   * 640px column the photo alone was 800px tall and the proof bar sat below
   * the fold on a 1440x900 display.
   */
  max-height: min(620px, 70vh);
}

@media (max-width: 899px) {
  .hf-hero__media img { aspect-ratio: 1 / 1; object-position: 50% 28%; }
  .hf-hero__figure::before { display: none; }
}

/*
 * The card laps the photograph by negative margin rather than by absolute
 * positioning, so it always reserves the room it needs. Same lesson as above:
 * an element taken out of flow cannot make its parent grow.
 */
.hf-hero__quote {
  position: relative;
  z-index: 2;
  margin: 0;
  background: var(--hf-black);
  color: var(--hf-on-dark);
  padding: 1.1rem 1.3rem;
  font-size: var(--hf-t-small);
  max-width: 330px;
}

@media (min-width: 900px) {
  .hf-hero__quote { margin-top: -5rem; margin-left: -1.75rem; }
}

.hf-hero__quote blockquote { margin: 0; line-height: 1.5; }

.hf-hero__quote figcaption {
  margin-top: 0.6rem;
  color: var(--hf-on-dark-muted);
  font-size: var(--hf-t-micro);
}

.hf-hero__stars {
  color: var(--hf-red-light);
  letter-spacing: 0.14em;
  margin-bottom: 0.45rem;
}

/* ==========================================================================
   Proof strip
   ========================================================================== */

/*
 * Dark, with the figures in red, per the mobile artboard. It was a pale
 * four-cell grid before, which is not what was approved and which let the
 * strongest numbers on the page read as footnotes.
 */
.hf-proof {
  background: var(--hf-black);
  color: var(--hf-on-dark);
  padding: clamp(1.25rem, 0.9rem + 1.5vw, 2rem) var(--hf-gutter);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr));
  gap: var(--hf-gap);
  text-align: center;
}

.hf-proof__figure {
  font-family: var(--hf-display);
  font-size: clamp(1.625rem, 1.25rem + 1.4vw, 2.25rem);
  color: var(--hf-red-light);
  line-height: 1.1;
}

.hf-proof__label {
  font-size: var(--hf-t-small);
  color: var(--hf-on-dark-muted);
  margin-top: 4px;
}

/*
 * Two up on a phone, figure beside its label rather than above it, which is
 * how the artboard draws it and which keeps the bar to two tidy rows instead
 * of four stacked cells.
 */
@media (max-width: 700px) {
  .hf-proof {
    grid-template-columns: 1fr 1fr;
    text-align: left;
    gap: 1.1rem 0.9rem;
  }

  .hf-proof > div {
    display: flex;
    align-items: baseline;
    gap: 8px;
  }

  /*
   * Smaller here than the clamp would give. These labels are longer than the
   * artboard's ("Helping San Antonio get fit" against "certified trainers"),
   * and at the full size they wrapped to three ragged lines.
   */
  .hf-proof__figure {
    flex: 0 0 auto;
    font-size: 1.375rem;
  }

  .hf-proof__label {
    margin-top: 0;
    line-height: 1.3;
    font-size: var(--hf-t-micro);
  }
}

/* ==========================================================================
   The four steps
   ========================================================================== */

.hf-steps { counter-reset: hf-step; }

.hf-step {
  display: flex;
  gap: clamp(0.875rem, 0.6rem + 1.1vw, 1.25rem);
  align-items: flex-start;
}

.hf-step__num {
  counter-increment: hf-step;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--hf-red);
  color: #fff;
  font-family: var(--hf-display);
  font-size: 1.25rem;
  display: grid;
  place-items: center;
}

.hf-step__num::before { content: counter(hf-step); }

.hf-step h3 { margin-bottom: 0.35em; }

/* ==========================================================================
   Cards
   ========================================================================== */

.hf-card {
  background: #fff;
  border: 1px solid var(--hf-line);
  display: flex;
  flex-direction: column;
}

.hf-card__media { overflow: hidden; }

.hf-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hf-card__body {
  padding: clamp(1.25rem, 1rem + 1vw, 1.75rem);
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex-grow: 1;
}

/* Service card, no image, icon at the top. */
/*
 * Result photos come from the clients' own phones, so they arrive at whatever
 * ratio they arrive at: Reka's pair is landscape, Samantha's portrait. A fixed
 * box with `contain` puts the two cards on the same rhythm without cropping
 * anybody.
 *
 * `cover` would be tidier at the edges and wrong here. These are full-body
 * before-and-afters, and clipping a head or a pair of feet is exactly the kind
 * of sloppiness that makes a real result look doctored.
 */
.hf-card--result .hf-card__media {
  aspect-ratio: 1 / 1;
  background: var(--hf-tint);
}

.hf-card--result .hf-card__media img { object-fit: contain; }

.hf-card--service { padding: clamp(1.25rem, 1rem + 1vw, 1.75rem); gap: 14px; }
.hf-card--service p { flex-grow: 1; color: var(--hf-grey-500); }

.hf-badge {
  display: inline-block;
  /* Never stretch. .hf-card is a flex column, so without this a badge fills
     the card width and reads as a broken button rather than a label. */
  align-self: flex-start;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 4px 8px;
  background: var(--hf-red);
  color: #fff;
}

.hf-badge--dark { background: var(--hf-black); }

.hf-badge--quiet {
  background: transparent;
  border: 1px solid var(--hf-line-warm);
  color: var(--hf-grey-500);
}

/* ==========================================================================
   Location card
   ========================================================================== */

.hf-location__media { height: 210px; }
.hf-location__media img { height: 210px; }

@media (max-width: 600px) {
  .hf-location__media,
  .hf-location__media img { height: 160px; }
}

.hf-location__address {
  font-size: 1.03125rem;
  line-height: 1.6;
  color: var(--hf-grey-800);
}

.hf-location__phone {
  font-family: var(--hf-display);
  font-size: 1.25rem;
}

.hf-location__phone a { color: var(--hf-ink); }

.hf-location__meta {
  font-size: var(--hf-t-small);
  color: var(--hf-grey-500);
  line-height: 1.55;
}

/*
 * Placeholder for assets the client still owes. Deliberately obvious: a
 * missing photo should look unfinished in review rather than pass as a design
 * decision and ship.
 */
.hf-placeholder {
  background: var(--hf-tint);
  border: 1px dashed var(--hf-line-warm);
  display: grid;
  place-items: center;
  text-align: center;
  padding: 12px;
  font-size: 0.78125rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--hf-grey-500);
  min-height: 120px;
}

/* ==========================================================================
   Trainers
   ========================================================================== */

.hf-trainer__media { aspect-ratio: 4 / 5; overflow: hidden; background: var(--hf-tint); }
.hf-trainer__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 20%; }

.hf-trainer__name { font-size: 1.3125rem; margin-top: 12px; }

/*
 * The specialty line. The structure map calls this the most important field on
 * the page: it is how a 58-year-old decides this is their gym without the site
 * ever mentioning age.
 */
.hf-trainer__specialty {
  font-size: 0.90625rem;
  font-weight: 600;
  color: var(--hf-red-dark);
  margin-top: 3px;
}

.hf-trainer__location {
  font-size: 0.875rem;
  color: var(--hf-grey-500);
  margin-top: 3px;
}

/* ==========================================================================
   Footer
   ========================================================================== */

.hf-footer {
  background: var(--hf-black);
  color: var(--hf-on-dark-muted);
  padding: var(--hf-section-y) var(--hf-gutter);
  font-size: var(--hf-t-small);
}

.hf-footer a { color: var(--hf-on-dark-muted); }
.hf-footer a:hover { color: #fff; }

.hf-footer__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: var(--hf-gap);
}

.hf-footer__heading {
  font-family: var(--hf-display);
  font-size: 1rem;
  color: #fff;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 10px;
}

.hf-footer__logo img { height: 42px; width: auto; }

.hf-footer__legal {
  margin-top: var(--hf-gap);
  padding-top: var(--hf-gap);
  border-top: 1px solid rgba(255,255,255,.12);
  font-size: var(--hf-t-micro);
  color: var(--hf-grey-300);
}

/* ==========================================================================
   Forms
   ========================================================================== */

.hf-form { display: grid; gap: 14px; }

.hf-form label {
  font-size: var(--hf-t-small);
  font-weight: 600;
  display: block;
  margin-bottom: 4px;
}

.hf-form input[type="text"],
.hf-form input[type="email"],
.hf-form input[type="tel"],
.hf-form select,
.hf-form textarea {
  width: 100%;
  font-family: var(--hf-body);
  font-size: var(--hf-t-body);
  padding: 13px 14px;
  border: 1px solid var(--hf-line-input);
  border-radius: var(--hf-radius);
  background: #fff;
  color: var(--hf-ink);
  /* 16px minimum or iOS Safari zooms the viewport on focus. */
  min-height: 50px;
}

.hf-form textarea { min-height: 120px; resize: vertical; }

/* ==========================================================================
   Print
   ========================================================================== */

@media print {
  .hf-utility,
  .hf-nav,
  .hf-hero__cta,
  .hf-btn { display: none; }

  .hf-page { background: #fff; color: #000; }
  .hf-section--dark { background: #fff; color: #000; }
  .hf-section--dark h1,
  .hf-section--dark h2,
  .hf-section--dark h3 { color: #000; }
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .hf-page *,
  .hf-page *::before,
  .hf-page *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ==========================================================================
   Video
   ========================================================================== */

.hf-video { max-width: 900px; }

.hf-video__player {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  display: block;
  background: #000;
}


/* ==========================================================================
   Artboard alignment, 18 Sep
   Differences found between the build and the mobile artboard, corrected.
   ========================================================================== */

/*
 * Steps: light numerals, not filled red discs.
 *
 * The artboard sets 01-04 in the display face at low contrast so the eye goes
 * to the step titles. Red discs pulled focus to the counters, which carry no
 * information at all.
 */
.hf-steps .hf-step__num {
  width: auto;
  height: auto;
  border-radius: 0;
  background: none;
  color: var(--hf-line-warm);
  font-family: var(--hf-display);
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: 0.02em;
}

.hf-steps .hf-step__num::before {
  content: "0" counter(hf-step);
}

/* Pillars: the verb stays ink, the noun goes red. */
.hf-pillar__accent { color: var(--hf-red-dark); }

/* InBody ticks are red in the artboard, not white. */
.hf-ticks { list-style: none; padding: 0; margin: 1.25rem 0 0; display: grid; gap: 10px; }
.hf-ticks li { display: flex; gap: 10px; align-items: baseline; }
.hf-ticks .hf-tick { color: var(--hf-red-light); flex: 0 0 auto; font-weight: 700; }

/*
 * Services: an icon, then a red text link rather than a full-width outlined
 * button. Four outlined buttons in a column read as four equal decisions; the
 * artboard makes them look like what they are, which is four ways in.
 */
.hf-service__icon { color: var(--hf-red-dark); margin-bottom: 2px; }

.hf-page .hf-service__link {
  font-weight: 700;
  font-size: var(--hf-t-small);
  color: var(--hf-red-dark);
  text-decoration: none;
  margin-top: auto;
}

.hf-page .hf-service__link:hover { text-decoration: underline; }

/* The service that is built but not switched on yet. */
.hf-card--held {
  background: transparent;
  border: 1px dashed var(--hf-line-warm);
}

.hf-card--held h3 { color: var(--hf-grey-500); }

/*
 * Closing call to action: red ground, black button.
 *
 * It was a dark section with a red button, which is the same treatment as the
 * InBody block and the video block above it. Three dark sections in a row and
 * the last one stops reading as an ending.
 */
.hf-cta--red {
  background: var(--hf-red);
  color: #fff;
}

.hf-cta--red h2 { color: #fff; }
.hf-cta--red .hf-lead { color: rgba(255,255,255,.92); }

.hf-page .hf-cta--red .hf-btn--primary {
  background: var(--hf-black);
  border-color: var(--hf-black);
  color: #fff;
}

.hf-page .hf-cta--red .hf-btn--primary:hover,
.hf-page .hf-cta--red .hf-btn--primary:focus-visible {
  background: #fff;
  border-color: #fff;
  color: var(--hf-black);
}

.hf-page .hf-cta--red .hf-btn--ghost {
  color: #fff;
  border-color: rgba(255,255,255,.75);
}

.hf-page .hf-cta--red .hf-btn--ghost:hover,
.hf-page .hf-cta--red .hf-btn--ghost:focus-visible {
  background: #fff;
  border-color: #fff;
  color: var(--hf-red-dark);
}

/* Location cards carry a badge in the artboard. */
.hf-location__title {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.hf-location__meta {
  font-size: var(--hf-t-small);
  color: var(--hf-grey-500);
  margin-top: 2px;
}

/*
 * The utility bar is desktop only. The artboard's phone header carries a call
 * button instead, and two full phone numbers above the logo pushed the
 * headline most of the way down a small screen.
 */
@media (max-width: 760px) {
  .hf-utility { display: none; }
}


/* ==========================================================================
   Utilities
   ==========================================================================

   Extracted from inline style attributes on 21 September 2026. Seventy of
   them came out of the templates; each rule below carries exactly the
   declarations the attribute carried, so rendering is unchanged.

   WHY THEY MOVED

   An inline style cannot be reached by the design controls in
   Settings > HASfit, cannot respond to a media query, cannot be overridden
   without !important, and would be blocked by a Content-Security-Policy.
   More practically: "tighten the spacing on the location pages" was a code
   deploy. Now it is a stylesheet edit.

   WHY FORTY ARE STILL INLINE

   Thirty-five inline styles deliberately override a component default -
   .hf-lead sets its own margin-top, .hf-placeholder its own min-height, and
   the attribute existed precisely to beat them. A single-class utility loses
   that fight on specificity, so extracting those blind would have changed how
   the pages look. They need the component itself reworked, with a rendered
   page to check against, which is a separate job. The remaining five contain
   PHP and are computed per render.

   NAMING

   Mechanical, not semantic: hf-u-<property abbreviation>-<value>, joined by
   a double hyphen where a rule sets more than one property. "p" stands in for
   a decimal point, so .hf-u-mt-p75rem is margin-top: .75rem. Ugly on purpose,
   because a name that describes the value cannot drift from it the way
   .hf-u-tight would.

   These sit last in the file so they win on source order against anything of
   equal specificity earlier.
   ========================================================================== */

.hf-u-bb-1px-solid-line { border-bottom:1px solid var(--hf-line); }
.hf-u-c-grey-400 { color:var(--hf-grey-400); }
.hf-u-c-ink { color:var(--hf-ink); }
.hf-u-c-red--ls-p1em { color:var(--hf-red); letter-spacing:.1em; }
.hf-u-d-flex--ai-center--gap-10px--fw2-wrap { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.hf-u-d-flex--gap-10px--fw2-wrap--m-0 { display:flex; gap:10px; flex-wrap:wrap; margin:0; }
.hf-u-fs-1p5rem { font-size:1.5rem; }
.hf-u-fs-t-h3 { font-size:var(--hf-t-h3); }
.hf-u-fst-normal { font-style:normal; }
.hf-u-fst-normal--lh-1p6 { font-style:normal; line-height:1.6; }
.hf-u-fst-normal--mt-1rem { font-style:normal; margin-top:1rem; }
.hf-u-fst-normal--mt-p6rem { font-style:normal; margin-top:.6rem; }
.hf-u-h-100pct { height:100%; }
.hf-u-ls2-none--p-0--m-0--lh-2 { list-style:none; padding:0; margin:0; line-height:2; }
.hf-u-m-0 { margin:0; }
.hf-u-m-0--fs-t-body--lh-1p6 { margin:0; font-size:var(--hf-t-body); line-height:1.6; }
.hf-u-m-0--fs-t-lead--lh-1p55 { margin:0; font-size:var(--hf-t-lead); line-height:1.55; }
.hf-u-mb-2rem { margin-bottom:2rem; }
.hf-u-mt--8px { margin-top:-8px; }
.hf-u-mt-10px { margin-top:10px; }
.hf-u-mt-14px { margin-top:14px; }
.hf-u-mt-1p25rem { margin-top:1.25rem; }
.hf-u-mt-1p5rem { margin-top:1.5rem; }
.hf-u-mt-1p5rem--fst-italic { margin-top:1.5rem; font-style:italic; }
.hf-u-mt-1p75rem { margin-top:1.75rem; }
.hf-u-mt-1p75rem--d-flex--gap-12px--fw2-wrap { margin-top:1.75rem; display:flex; gap:12px; flex-wrap:wrap; }
.hf-u-mt-1p75rem--d-flex--gap-12px--jc-center--fw2-wrap { margin-top:1.75rem; display:flex; gap:12px; justify-content:center; flex-wrap:wrap; }
.hf-u-mt-1rem--c-on-dark-muted { margin-top:1rem; color:var(--hf-on-dark-muted); }
.hf-u-mt-2rem { margin-top:2rem; }
.hf-u-mt-auto { margin-top:auto; }
.hf-u-mt-p4rem { margin-top:.4rem; }
.hf-u-mt-p6rem { margin-top:.6rem; }
.hf-u-mt-p75rem { margin-top:.75rem; }
.hf-u-mt-p85rem { margin-top:.85rem; }
.hf-u-mw-420px { max-width:420px; }
.hf-u-p-1rem { padding:1rem; }
.hf-u-pos-relative--ar-16-10--bg-tint--ov-hidden--br-radius-3px { position:relative; aspect-ratio:16/10; background:var(--hf-tint); overflow:hidden; border-radius:var(--hf-radius,3px); }
.hf-u-pt-0 { padding-top:0; }
.hf-u-ta-center { text-align:center; }
.hf-u-ta-left { text-align:left; }
.hf-u-ta-left--p-8px-0--fw-600 { text-align:left; padding:8px 0; font-weight:600; }
.hf-u-ta-right--p-8px-0--c-grey-500 { text-align:right; padding:8px 0; color:var(--hf-grey-500); }
.hf-u-w-100pct--mt-p75rem--bc-collapse { width:100%; margin-top:.75rem; border-collapse:collapse; }
