/* ==========================================================================
   Foster & Length Studio — site-specific CSS
   Per Form 18 Section 11: all new rules for this build go here, never inline,
   never edited directly into the shared base/components/pages files copied
   from the reference template.
   ========================================================================== */

/* ==========================================================================
   TYPE SCALE — Typography Option A, chosen 2026-07-28
   Cormorant Garamond (display) + Jost (UI and body).

   The font roles are declared in includes/code-head.php. This block retunes
   the scale, because base.css was tuned for Outfit 600/700, a geometric sans
   with a large x-height. Cormorant is a Garamond: small x-height, light
   colour on the page, high stroke contrast. At Outfit's sizes and weights it
   reads cramped and heavy, so headings go up in size and down in weight.

   Only h1, h2 and h3 take the serif. h4, h5 and h6 stay on Jost, along with
   every small-UI role the shared stylesheets point at --font-heading
   (buttons, nav links, form labels, tags, badges). h6 in particular is the
   uppercase eyebrow label used across the site, and a display serif set small
   and uppercase loses its whole reason for being there.
   ========================================================================== */

/* Size is inherited from base.css at 17px, deliberately. A 19px/weight-400
   version was tried on 2026-07-28 and reverted at the operator's call: it made
   the page read heavier than the direction wants. The light setting stands.

   Note that body copy still gained readability that day from the separate
   --clr-muted contrast fix in client-config.php, which most paragraphs on this
   site are painted with. That change was kept. */
body {
  font-weight: 300;
  line-height: 1.75;
  letter-spacing: 0.005em;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
}

/* h1 only, pulled back from an earlier 3.5rem / 4.8vw setting. On a laptop that
   landed near 56px and ran a normal headline to six lines. This is the one size
   change from the 2026-07-28 pass that was kept; h2 and body went back to their
   earlier values, so the step from h1 to h2 is deliberately a long one. */
h1 {
  font-size: clamp(2rem, 3.6vw, 2.75rem);
  font-weight: 300;
  line-height: 1.15;
  letter-spacing: -0.005em;
}

h2 {
  font-size: clamp(1.75rem, 3.2vw, 2.25rem);
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: 0;
}

h3 {
  font-size: clamp(1.25rem, 2.2vw, 1.625rem);
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: 0;
}

/* .section-title is an h2 in every use across this site, but base.css styles it
   as a class and so bypasses the h2 rule above entirely: it sets its own
   font-size, its own font-weight: 700, and — the part that actually bites —
   color: var(--clr-dark), which is in the fixed-palette block in code-head.php
   and NOT driven by client-config.php.

   The effect was two different heading colours on one page from two different
   files, only one of which answers to $colorHeadings. Changing the heading colour
   in client-config.php moved the bare h2s and left every section title behind.
   That is the exact failure this override exists to remove.

   Inheriting all three properties from the h2 rule is deliberate: after this,
   $colorHeadings in client-config.php controls every heading on the site, which
   is how it was supposed to work. Do not reintroduce a colour here. */
.section-title {
  font-size: clamp(1.75rem, 3.2vw, 2.25rem);
  font-weight: 400;
  color: var(--clr-headings);
}

/* Jost carries less weight on screen than Outfit at the same numeric value,
   so the sans roles sit one step up and lean on tracking instead of bulk. */
h4,
h5 {
  font-weight: 500;
  letter-spacing: 0.01em;
}

h6 {
  font-weight: 500;
  letter-spacing: 0.16em;
}

strong,
b {
  font-weight: 500;
}

/* Buttons and section labels: wide tracking is what makes a light sans read as
   deliberate rather than underweight. This is the quiet-luxury signal doing
   the work that a heavier weight would do in the template's default look. */
.btn {
  font-weight: 400;
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.section-label {
  font-weight: 500;
  letter-spacing: 0.16em;
}

/* Text fallback for the nav logo — used until a real logo file exists.
   See includes/navigation.php and $imgLogo in includes/client-config.php.
   Takes the serif: a wordmark is a display moment, unlike the nav links
   beside it, which stay on Jost. */
.nav-logo-text {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.5rem;
  color: var(--clr-headings);
  letter-spacing: 0.04em;
}

/* ==========================================================================
   MOBILE HEADER — two fixes, 2026-08-06
   Both are overrides of css/nav.css and css/header.css. Neither of those
   files is touched: they are at reference-template parity and a sync would
   overwrite an edit made there.

   NOTE ON THE BREAKPOINT. js/navigation.js switches the nav to portrait at
   1200px (its mobileBreakpoint), but header.css only re-pads the body below
   900px. So between 901px and 1200px the nav is already the short mobile bar
   while the body is still padded for the 70px desktop one. That gap existed
   before these rules and is why the media query below is 1200px, not 900px.
   The two numbers have to agree or the header either overlaps the page or
   floats above a strip of nothing.
   ========================================================================== */

/* 1 · The logo was 30px tall on mobile against 82px on desktop, which is 37%
   and reads as an afterthought on the one screen most visitors use. Raised to
   44px on 2026-08-06, then to 54px later the same day at the operator's call.
   The bar went 48px to 64px to 74px alongside it, and the body padding in the
   media query below has to match the bar or the header either overlaps the
   page or floats above a strip of nothing.

   54px is close to the practical ceiling. The wordmark is 888x223, so at 54px
   it is about 215px wide, and with the logo padding and the hamburger that is
   roughly 300px of a 360px phone. Much past this and the two collide on the
   narrowest handsets.

   This still does NOT fix the "HAIR EXTENSIONS" subline, which is about 3px
   tall at 30px and about 6px at 54px. Better, still not readable. That needs a
   wordmark-only logo variant, which is a separate job on the pre-launch list.
   Making the bar tall enough to render that subline properly would eat a
   serious amount of a phone screen, so size alone will not get there. */
.navigation-portrait {
  height: 74px;
}

.navigation-portrait .nav-logo > img {
  height: 54px;
}

@media (max-width: 1200px) {
  body {
    padding-top: 74px;
  }

  body.has-hero-slider {
    padding-top: 0;
  }
}

/* 2 · "Request a Callback" ran off the right edge of the mobile drawer.

   A specificity collision, not a sizing mistake. nav.css sets the button to
   width: calc(100% - 3rem) with 1.5rem margins, which fits exactly. But
   .navigation-portrait .nav-menu > li > a (0,2,1) beats
   .navigation-portrait .nav-button (0,2,0) and forces width: 100% back on,
   so the element became full width PLUS 3rem of margin and overflowed by
   exactly that much. The same rule also overrode the centring and padding,
   which is why the label sat left of centre.

   Fixed by matching on both, a.nav-button at 0,3,1, and using width: auto
   with display: flex so the margins do the insetting rather than a
   calc() that another rule can win. */
.navigation-portrait .nav-menu > li > a.nav-button {
  display: flex;
  width: auto;
  margin: 1rem 1.5rem;
  padding: 0.75rem 1.125rem;
  justify-content: center;
}

/* Photo placeholder — used everywhere a Visual Placeholder Block calls for a
   real photo that doesn't exist yet (see Form 18 punch list #4). Drop an
   <img> in and remove this class once real photography exists; layout
   (aspect-ratio, radius, shadow) is already handled by the parent container
   (.split-image, .service-card__image, etc.) so this only needs to fill it. */
.img-placeholder {
  width: 100%;
  height: 100%;
  min-height: 220px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: linear-gradient(135deg, #f3efe9 0%, #e3ddd2 100%);
  color: var(--clr-muted);
  font-size: 0.8125rem;
  line-height: 1.5;
  padding: 1.5rem;
}

.img-placeholder__label {
  max-width: 220px;
}

.img-placeholder__label i {
  display: block;
  font-size: 1.5rem;
  margin-bottom: 0.5rem;
  opacity: 0.6;
}

/* ==========================================================================
   FULL-BLEED ROWS (.fb-split) — layout direction decided 2026-07-28

   Replaces the contained pattern (.container > .split-section) as the primary
   layout device on this site. The old pattern capped everything at 1280px with
   a 4:3 image beside a text column, which is the reference template's default
   and reads as a standard service site. The reference screenshots in
   _info/Design Inspiration/ all run their halves edge to edge instead, and that
   is the single cue being taken from them. Type, palette, nav and logo are
   unchanged.

   ONE COMPONENT, THREE JOBS. The same row is the hero (--tall), the
   problem/solution sections, and the process steps (--num). That is deliberate:
   it is why the process no longer needs a row of three identical cards, which
   the writing rules ban anyway, and why a step can carry a full-height photo
   instead of a thumbnail.

   Source order is always content first, image second. On desktop that reads
   left to right; on mobile it stacks text above its image, which is what
   _01-visual-psychology.md Part 8.2 asks for on process steps. Use --reverse
   to flip the desktop columns without changing source order.
   ========================================================================== */

.fb-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  width: 100%;
}

.fb-split__content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(2.5rem, 5.5vw, 5.5rem) clamp(1.5rem, 4.5vw, 4.5rem);
  background: var(--clr-white);
}

/* Full-bleed halves get wide on a large monitor, and a text column that wide
   is unreadable. The measure is capped here rather than on the row. */
.fb-split__inner {
  width: 100%;
  max-width: 34rem;
}

.fb-split__image {
  position: relative;
  min-height: 30rem;
  overflow: hidden;
  background: var(--clr-light);
}

.fb-split__image > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The dev placeholder fills the slot the same way the real photo will.
   Overrides the width/height sizing in .vp-block below for this container
   only, per _01-visual-psychology.md Part 5A.1, which allows either technique
   as long as it matches what the slot already uses.

   z-index keeps it above the --soft seam gradient, so the dev block stays fully
   legible while a real <img> underneath still gets feathered. */
.fb-split__image > .vp-block {
  position: absolute;
  inset: 0;
  z-index: 2;
}

/* ---------- Modifiers ---------- */

.fb-split--tall .fb-split__image {
  min-height: min(78vh, 46rem);
}

.fb-split--reverse .fb-split__content {
  grid-column: 2;
  grid-row: 1;
}

.fb-split--reverse .fb-split__image {
  grid-column: 1;
  grid-row: 1;
}

.fb-split--tint .fb-split__content {
  background: var(--clr-base);
}

/* ---------- Soft seam (--soft) ---------- */

/* By default a row has a hard vertical edge where the solid content column meets
   the photo. Screenshot 07 in _info/Design Inspiration/ does the opposite: the
   picture carries on behind the headline and there is no seam to find. --soft
   feathers the image's leading edge into whatever colour the content column is
   painted, so the two halves read as one photograph, without restructuring the
   grid or moving the image out of its slot.

   --fb-seam tracks the content background. Any future background modifier needs
   to set it too, or the fade will land on the wrong colour. Interpolation to
   `transparent` is premultiplied per CSS Images 3, so there is no grey ghost
   in the middle of the ramp. */
.fb-split {
  --fb-seam: var(--clr-white);
}

.fb-split--tint {
  --fb-seam: var(--clr-base);
}

.fb-split--dark {
  --fb-seam: var(--clr-dark);
}

.fb-split--soft .fb-split__image::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to right, var(--fb-seam), transparent 24%);
}

/* Image sits left on a reversed row, so the feathered edge moves to the right. */
.fb-split--soft.fb-split--reverse .fb-split__image::before {
  background: linear-gradient(to left, var(--fb-seam), transparent 24%);
}

/* ---------- Full-bleed background (--bleed) ---------- */

/* The full-bleed hero variant asked for on 2026-07-22 and unbuilt until
   2026-07-29: one image spanning the whole row with the copy sitting over it,
   rather than two halves side by side. --soft feathers the seam between two
   columns; --bleed removes the second column entirely and the seam with it.

   Built for a landscape image whose subject sits to one side. The scrim runs
   from the copy edge and clears before the subject, so the picture is never
   flattened under a full-width wash. Swapping the <img> for a <video> needs no
   other change, which is the other half of what 2026-07-22 asked for. */
.fb-split--bleed {
  position: relative;
  grid-template-columns: 1fr;
}

.fb-split--bleed .fb-split__image,
.fb-split--bleed.fb-split--tall .fb-split__image {
  position: absolute;
  inset: 0;
  grid-column: 1;
  grid-row: 1;
  min-height: 0;
  z-index: 0;
}

/* The row's height comes from the copy now, because an absolutely positioned
   image cannot give a grid row any height of its own. */
.fb-split--bleed.fb-split--tall .fb-split__content {
  min-height: min(78vh, 46rem);
}

.fb-split--bleed .fb-split__content {
  position: relative;
  grid-column: 1;
  grid-row: 1;
  z-index: 2;
  background: transparent;
  max-width: min(100%, 62rem);
}

/* Legibility scrim: above the photo, below the copy. Light rather than dark,
   because the studio in the picture is bright and warm and a dark overlay would
   fight the whole point of showing it.

   The literal rgba white here is deliberate and is not a palette colour. It is a
   veil over a photograph, so it has to be a neutral at a specific alpha, and
   running four stops through color-mix() to say "white" would be less readable,
   not more. If the brand ever wants a tinted veil, that is the moment to make it
   a variable. */
.fb-split--bleed .fb-split__image::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to right,
    rgba(255, 255, 255, 0.95) 0%,
    rgba(255, 255, 255, 0.88) 26%,
    rgba(255, 255, 255, 0.45) 48%,
    rgba(255, 255, 255, 0) 68%);
}

/* Bias the crop right of centre so the stylist and client stay in frame when a
   16:9 source is cropped into a taller box. */
.fb-split--bleed .fb-split__image > img {
  object-position: 60% center;
}

/* ---------- Section band that introduces a run of rows ---------- */

/* Fixes a real blend reported 2026-07-29. The "Three stages, built in order"
   header sat on white, and stage 01's content half is also white, so on the
   right-hand side the two ran together with no boundary and the header read as
   floating in a gap rather than introducing anything.

   Tinting it solves that, but --clr-base cannot do it: the problem-statement row
   directly above already uses --clr-base for its content half, also on the
   right, so base-on-base would just move the blend up one row. --clr-accent-light
   is a pale bronze, distinct from both white below and --clr-base above. It was
   added to the palette on 2026-07-28 and nothing had referenced it until now.

   Use this for any band that introduces a sequence of .fb-split rows, not as a
   general-purpose tint. Its whole job is being the third colour.

   NAMED .fl-band, NOT .section-accent. The first attempt at this used
   .section-accent and shipped a white heading on a pale background. base.css
   already defines .section-accent as a solid bronze band with white text,
   including an explicit `.section-accent h1, h2, h3 { color: #fff }`. Overriding
   only background-color left that white heading rule in force, and it outranks
   .section-title on specificity, so the h2 went invisible.

   base.css owns the whole .section-* namespace: -sm, -xl, -dark, -base, -white,
   -brun, -accent, -header, -label, -title, -subtitle. Do not add to it from this
   file. Anything new here takes the .fl- prefix, the same way .fl-feed does. */
.fl-band {
  background-color: var(--clr-accent-light);
}

/* ---------- Hero rotator (.hero-rotator) ---------- */

/* Three crossfading stills inside the hero's image slot, added 2026-07-29.
   Same crossfade shape and timing convention as the shared page-background
   rotator, but scoped to the hero rather than the viewport.

   WHY NOT THE SHARED ROTATOR. css/bg-rotator-1and3-img.css cannot do this job.
   It is `position: fixed; inset: 0`, so it paints behind the entire page
   including the footer, not inside a section. It is also `display: none` below
   1024px, which would leave every phone and tablet with no hero image at all.
   And it wants literal 1.jpg/2.jpg/3.jpg plus a pattern.png that does not
   exist. It stays untouched and unused on the content pages; this is a separate,
   section-scoped component.

   Three slides, each visible about 6.3s of a 21s cycle with a crossfade either
   side. Both values are variables: change --hero-per and keep
   --hero-cycle equal to slides x --hero-per, or the last slide never gets its
   turn. That arithmetic is the same trap the page rotator hit at six slides. */
.hero-rotator {
  --hero-per: 7s;
  --hero-cycle: 21s; /* 3 slides x 7s */
  position: absolute;
  inset: 0;
  z-index: 0; /* below the --bleed scrim, which is z-index 1 */
}

.hero-rotator img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 60% center;
  opacity: 0;
  backface-visibility: hidden;
  transform: translateZ(0);
  will-change: opacity;
  animation: heroRotate var(--hero-cycle) linear infinite;
  animation-delay: calc(var(--i, 0) * var(--hero-per));
}

.hero-rotator img:nth-child(1) { --i: 0; }
.hero-rotator img:nth-child(2) { --i: 1; }
.hero-rotator img:nth-child(3) { --i: 2; }

@keyframes heroRotate {
  0%   { opacity: 0; animation-timing-function: ease-in; }
  10%  { opacity: 1; animation-timing-function: ease-out; }
  30%  { opacity: 1; }
  40%  { opacity: 0; }
  100% { opacity: 0; }
}

/* Hold on the first frame rather than animating. Matches what the shared
   rotator does, and a slow crossfade behind a headline is exactly the kind of
   motion this setting exists to stop. */
@media (prefers-reduced-motion: reduce) {
  .hero-rotator img {
    animation: none;
    opacity: 0;
  }

  .hero-rotator img:nth-child(1) {
    opacity: 1;
  }
}

/* Dark content panel beside a full-bleed photo. This is the one structural
   idea taken from screenshot 01, the mesh-integration page, and it is held
   back for moments that earn it rather than used as a default. */
.fb-split--dark .fb-split__content {
  background: var(--clr-dark);
}

.fb-split--dark .fb-split__content h1,
.fb-split--dark .fb-split__content h2,
.fb-split--dark .fb-split__content h3 {
  color: var(--clr-footer-headings);
}

.fb-split--dark .fb-split__content p,
.fb-split--dark .fb-split__content li {
  color: var(--clr-footer-text);
}

/* The main bronze only reaches about 3.9:1 on this ground and fails AA, so
   light-on-dark accents use their own value. See $colorAccentOnDark. */
.fb-split--dark .fb-split__content .section-label,
.fb-split--dark .fb-split__num {
  color: var(--clr-accent-on-dark);
}

/* ---------- Step number, for process sequences ---------- */

.fb-split__num {
  display: block;
  font-family: var(--font-display);
  font-size: 3rem;
  font-weight: 300;
  line-height: 1;
  color: var(--clr-accent);
  margin-bottom: 0.75rem;
}

/* ---------- Mobile ---------- */

@media (max-width: 900px) {
  .fb-split {
    grid-template-columns: 1fr;
  }

  /* Reverse is a desktop-only concern. On one column, source order wins and
     every row reads the same way: text, then its image. */
  .fb-split--reverse .fb-split__content,
  .fb-split--reverse .fb-split__image {
    grid-column: 1;
    grid-row: auto;
  }

  .fb-split__image,
  .fb-split--tall .fb-split__image {
    min-height: 20rem;
  }

  .fb-split__inner {
    max-width: none;
  }

  /* One column, so there is no vertical seam to hide. A horizontal fade here
     would just eat the top of the photograph. */
  .fb-split--soft .fb-split__image::before {
    display: none;
  }

  /* The copy now sits over the whole width, so a left-to-right scrim would
     leave half the text on bare photograph. Runs top to bottom instead, and
     never clears completely. */
  .fb-split--bleed .fb-split__content {
    max-width: none;
  }

  .fb-split--bleed.fb-split--tall .fb-split__content {
    min-height: 32rem;
  }

  .fb-split--bleed .fb-split__image::after {
    background: linear-gradient(to bottom,
      rgba(255, 255, 255, 0.95) 0%,
      rgba(255, 255, 255, 0.84) 48%,
      rgba(255, 255, 255, 0.58) 100%);
  }
}

/* ==========================================================================
   PORTRAIT IMAGE MODIFIERS — visual direction decided 2026-07-22
   (_info/_visual-direction.md). Long hair is a portrait subject; the shared
   components.css defaults (.split-image, .service-card__image) are fixed to
   landscape ratios. These modifiers override the ratio only, applied
   alongside the base class, without touching the shared file itself.
   ========================================================================== */

.split-image--portrait {
  aspect-ratio: 3 / 4;
}

.service-card__image--portrait {
  aspect-ratio: 3 / 4;
}

/* Portrait images are noticeably taller than a typical short text column at
   equal grid width. .split-section's default align-items:center then leaves
   a large empty gap above short text blocks (vertically centered against a
   much taller row). Top-align text instead whenever a portrait image is
   present, so it starts level with the image instead of floating mid-frame. */
.split-section:has(.split-image--portrait) {
  align-items: start;
}

/* ---------- Portrait column width, fixed 2026-08-02 ----------
   The rendered height of one of these images is not set by the image file at
   all. It is computed: .split-section is a 1fr 1fr grid, so the image column
   is half the container, and aspect-ratio 3/4 turns that width into a height.
   At the 1280px container that was a 580px column and a 773px tall image,
   whatever the file's own dimensions were. Replacing the file changed nothing,
   because .split-image img is width:100% height:100% object-fit:cover and is
   stretched to fill the box either way.

   So the dial is the column width, not the file. Narrowing the image column
   also gives the copy more room, which suits the Problem section on
   Extensions.php where the text runs to four paragraphs.

   Inside a min-width query on purpose: this selector is more specific than the
   `.split-section { grid-template-columns: 1fr }` mobile collapse in
   components.css and would otherwise beat it and break the stack under 900px. */
@media (min-width: 901px) {
  .split-section:has(.split-image--portrait) {
    grid-template-columns: 1.35fr 0.65fr;
  }

  /* Belt and braces on very wide screens, where 0.65fr still grows. */
  .split-image--portrait {
    max-width: 26rem;
    justify-self: end;
  }
}

/* ==========================================================================
   STUDIO FEED (.fl-feed) — full-bleed portrait carousel

   Placement decided 2026-07-22 (_info/_visual-direction.md): Home, near the
   hero, as a behind-the-scenes strip. Mechanics taken from screenshot 04 in
   _info/Design Inspiration/ — edge to edge, tiles running off both sides so it
   reads as scrollable, one quiet caption line above, a single arrow control.

   Two deliberate departures from that screenshot. Its tiles are mixed aspect,
   some square and some portrait, which reads scrappy; these are uniform 3:4 to
   match the portrait direction the rest of the site follows. And its caption is
   a hashtag solicitation, which is retail voice and banned by Form 18 Section 06.

   Runs on the Slick already vendored in /slick and already loaded by
   includes/code-head.php and includes/code-bot.php. No new dependency.
   ========================================================================== */

.fl-feed {
  overflow: hidden;
}

.fl-feed__intro {
  max-width: 34rem;
  margin: 0 auto 2.5rem;
  padding: 0 1.5rem;
  text-align: center;
}

.fl-feed__intro p {
  margin-bottom: 0;
}

/* The track is full-bleed on purpose: it is not inside .container. Tiles that
   run off both edges are what signals the strip scrolls. */
.fl-feed__track {
  position: relative;
}

.fl-feed__item {
  position: relative;
  aspect-ratio: 3 / 4;
  margin: 0 0.5rem;
  overflow: hidden;
  background: var(--clr-light);
}

.fl-feed__item > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.fl-feed__item > .vp-block {
  position: absolute;
  inset: 0;
}

/* Slick's default arrows sit outside the track (left: -25px), which puts them
   off-screen on a full-bleed strip. Pulled inside and quietened down. */
.fl-feed__track .slick-prev,
.fl-feed__track .slick-next {
  z-index: 2;
  width: 44px;
  height: 44px;
}

.fl-feed__track .slick-prev {
  left: 1rem;
}

.fl-feed__track .slick-next {
  right: 1rem;
}

.fl-feed__track .slick-prev::before,
.fl-feed__track .slick-next::before {
  color: var(--clr-dark);
  font-size: 26px;
  opacity: 0.65;
}

.fl-feed__track .slick-prev:hover::before,
.fl-feed__track .slick-next:hover::before {
  opacity: 1;
}

@media (max-width: 600px) {
  .fl-feed__item {
    margin: 0 0.25rem;
  }
}

/* ==========================================================================
   BACKGROUND ROTATOR — file paths and overlay, 2026-07-29

   Installed on the content pages 2026-07-29 to match examplecorp-claude, where
   the fixed rotator sits behind everything and shows through the translucent
   header and footer. Mechanics all live in the shared
   css/bg-rotator-1and3-img.css and are not duplicated here. Only two things
   differ on this site.

   WHAT YOU ACTUALLY SEE. The rotator is `position: fixed` behind `.page-wrap`,
   so it is only visible where page content is transparent. Every section on this
   site has an opaque background, which means the rotator shows through the
   header and the footer and nowhere else. That is the intended effect, not a
   half-finished one. The header over Home reveals the hero photograph beneath
   it rather than the rotator, because the hero is page content and sits above.

   The shared stylesheet also hides the rotator entirely below 1024px. That is
   template behaviour, left alone.

   1. FILE PATHS. The shared rules point at 1.jpg, 2.jpg and 3.jpg. This site's
      background images are named hero.jpg, hero02.jpg and hero03.jpg, so the
      three background-image declarations are re-pointed below. Renaming the
      files instead would delete these three rules. Timing is untouched: the
      shared default is already 3 slides x 9s = 27s.

      These are the same three stills the hero rotator uses. Harmless, because
      the two are never visible at the same moment, but dedicated background
      images would be better than reusing hero shots. Worth revisiting when real
      photography lands.

   2. NO OVERLAY PATTERN. The shared `:after` rule loads images/bg/pattern.png,
      which does not exist here, so it is switched off rather than 404ing on
      every page load. Delete this override the day a real pattern.png lands.
   ========================================================================== */

.rotating-bg-parallax li:nth-child(1) span {
  background-image: url(../images/bg/hero.jpg);
}

.rotating-bg-parallax li:nth-child(2) span {
  background-image: url(../images/bg/hero02.jpg);
}

.rotating-bg-parallax li:nth-child(3) span {
  background-image: url(../images/bg/hero03.jpg);
}

.rotating-bg-parallax:after {
  background-image: none;
}

/* ==========================================================================
   FOOTER — widened and warmed to brown, 2026-07-29

   Site-specific overrides of the shared css/footer.css, which stays at
   reference-template parity per Form 18 Section 11. Everything here is loaded
   after it and wins.

   1. WIDTH. Every row above the footer now runs edge to edge (.fb-split), so a
      1280px footer read as a narrow box bolted onto a full-bleed page. The three
      inner containers move to --container-wide, 1440px, which already exists in
      the palette for exactly this. Deliberately not made full-bleed: footer
      columns are text, and text spread across a 2560px monitor stops being
      readable. If more width is wanted it is one value, not a rewrite.

   2. COLOUR. Warm brown instead of near-black, derived from the site's own
      bronze accent #8a6d4c darkened along the same hue rather than picked
      fresh, so the footer reads as the same family as the buttons and links.

      WHY NOT THE BRONZE ITSELF: #8a6d4c cannot be the footer background while
      the copy stays light. The footer body text on it measures about 2.9:1 and
      fails WCAG AA, which wants 4.5:1 for normal text. The depths below measure
      5.7:1 and better. Putting the brighter bronze back there means flipping all
      the footer copy to dark, which is a different design, not a colour swap.

   The neutral greys inherited from the template are cold and read grey-on-brown,
   so each one moves to its warm equivalent. The bottom bar copy also went up in
   contrast: it was #4a4a48 on near-black, roughly 1.9:1, which failed AA badly
   and covers real content (copyright, Terms, Privacy, Credits). Same reasoning
   as the --clr-muted fix on 2026-07-28.

   3. NO HEXES. Simplified 2026-07-29. The first version of this block declared
      six of its own variables here and hardcoded eight more colours inline,
      which meant repainting the footer took a hunt through this file. Every
      colour below now resolves to client-config.php, which is the whole point of
      that file: one hex there, whole site, however many pages. Nothing in this
      block should ever be a literal colour again.
   ========================================================================== */

/* ---------- Width ---------- */

.ft-main-wrap,
.ft-ft-inner,
.rl-band__inner {
  max-width: var(--container-wide);
}

/* ---------- Body ---------- */

/* 95% rather than solid, matching examplecorp-claude's rgba(17,17,16,0.95). The
   background rotator is fixed behind the page, so this is what lets it show
   faintly through the footer and stops the bottom of the page reading as a dead
   slab. Same reason the header runs at 57% white with an 8px blur.
   color-mix keeps the alpha without reintroducing a literal colour, so the hue
   still follows $colorFooterBg in client-config.php. */
#footerCont {
  /* background: color-mix(in srgb, var(--clr-footer-bg) 95%, transparent); */
  background: rgba(17, 17, 16, 0.95); /*Darker*/
  color: var(--clr-footer-text);
}

.ft-main-wrap h4 {
  border-bottom-color: var(--clr-footer-rule);
}

.ft-main-wrap a:link,
.ft-main-wrap a:visited,
.ft-contact-item,
.ft-about-text {
  color: var(--clr-footer-text-dim);
}

.ft-main-wrap a:hover,
.ft-ft-right a:hover,
.rl-band__list a:hover {
  color: var(--clr-footer-headings);
}

.ft-contact-item i,
.rl-band__list li::after {
  color: var(--clr-accent-on-dark);
}

/* ---------- Social icons ---------- */

.soc-med a {
  background-color: var(--clr-footer-rule);
  color: var(--clr-footer-text-dim);
}

/* ---------- Bottom bar ---------- */

.ft-ft-cont {
  background-color: var(--clr-footer-bg-deep);
  border-top-color: var(--clr-footer-rule);
}

.ft-ft-left,
.ft-ft-right,
.ft-ft-right a:link,
.ft-ft-right a:visited,
.rl-band__heading {
  color: var(--clr-footer-text-dim);
}

/* ---------- Related links band ---------- */

/* Stays translucent, which was this build's own deliberate departure from the
   reference template and the reason it must survive a template sync. color-mix
   keeps the alpha without reintroducing a literal colour, so the hue still
   follows $colorFooterBg.

   Worth reviewing: with the background rotator absent from the content pages
   there is nothing behind this band to show through, so the translucency may no
   longer be buying anything. Left alone rather than quietly discarding a
   recorded decision. */
.rl-band {
  background: color-mix(in srgb, var(--clr-footer-bg) 90%, transparent);
  border-top-color: color-mix(in srgb, var(--clr-footer-rule) 60%, transparent);
}

.rl-band__list a:link,
.rl-band__list a:visited {
  color: var(--clr-footer-text-dim);
}

/* ==========================================================================
   VISUAL PLACEHOLDER BLOCK — dual-layer standard, _01-visual-psychology.md Part 5A
   Dev-only. Remove every .vp-block from HTML and these classes from this
   stylesheet before go-live (the Layer 2 HTML comment beneath each block stays,
   it needs no cleanup).

   Sits INSIDE the actual image slot (.split-image, .service-card__image),
   filling it at 100% width/height, standing in for the missing photo. Those
   parent containers are fixed aspect-ratio boxes with overflow:hidden, so
   .vp-block is a flex column sized to match exactly, and only the prompt text
   itself scrolls internally (.vp-nb-prompt-text) — the warning label, file
   path, size, and Copy button stay pinned and visible without scrolling.
   ========================================================================== */

.vp-block {
  width: 100%;
  height: 100%;
  min-height: 220px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  padding: 0.5rem 0.625rem 0.625rem;
  border: 2px dashed #c99a2e;
  background: #fffbeb;
  font-size: 0.6875rem;
  color: var(--clr-text);
  overflow: hidden;
}

.vp-block-label {
  flex-shrink: 0;
  display: block;
  font-weight: 700;
  color: #8a5a00;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  font-size: 0.5625rem;
  margin-bottom: 0.25rem;
}

.vp-block-file {
  flex-shrink: 0;
  font-size: 0.625rem;
  line-height: 1.4;
  margin-bottom: 0.5rem;
}

.vp-nb-prompt {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid #93c5fd;
  border-radius: var(--radius-sm);
  background: #eff6ff;
  overflow: hidden;
}

.vp-nb-header {
  flex-shrink: 0;
  display: block;
  background: #dbeafe;
  color: #1e3a8a;
  font-weight: 700;
  text-transform: uppercase;
  font-size: 0.5625rem;
  letter-spacing: 0.01em;
  padding: 0.3rem 0.5rem;
}

.vp-copy-btn {
  flex-shrink: 0;
  align-self: flex-end;
  margin: 0.3rem 0.4rem;
  font-size: 0.6875rem;
  padding: 0.2rem 0.55rem;
  border: 1px solid #1e40af;
  background: #fff;
  color: #1e40af;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.vp-copy-btn:hover {
  background: #1e40af;
  color: #fff;
}

.vp-nb-prompt-text {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  white-space: pre-wrap;
  font-family: Consolas, 'SFMono-Regular', Menlo, monospace;
  font-size: 0.625rem;
  line-height: 1.45;
  color: #1e293b;
  padding: 0 0.5rem 0.5rem;
  margin: 0;
}


/* ============================================================
   FAQ  ·  The Process page
   ------------------------------------------------------------
   Native <details>/<summary> rather than the .faq-item /
   .faq-question / .faq-answer set in pages.css. That set styles
   .faq-answer as display:none and there is no toggle script
   anywhere in this site, so answers written with those classes
   would never become visible. Native elements need no JS, are
   keyboard accessible by default, and survive a script failure
   with the content still readable.

   Colour comes from --clr-* only. No literal brand hex here.
   ============================================================ */

.fl-faq {
  border-top: 1px solid var(--clr-border);
}

.fl-faq__item {
  border-bottom: 1px solid var(--clr-border);
}

.fl-faq__q {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.25rem;
  cursor: pointer;
  list-style: none;
  padding: 1.375rem 0;
  font-family: var(--font-heading);
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.45;
  color: var(--clr-headings);
  transition: color 0.2s ease;
}

.fl-faq__q::-webkit-details-marker { display: none; }

.fl-faq__q:hover,
.fl-faq__item[open] .fl-faq__q {
  color: var(--clr-accent);
}

/* Chevron. Rotates on open, and is decorative only — the summary
   element already carries the expanded state for screen readers. */
.fl-faq__q::after {
  content: "";
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  margin-top: 0.3rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transform-origin: center;
  transition: transform 0.25s ease;
}

.fl-faq__item[open] .fl-faq__q::after {
  transform: rotate(-135deg);
}

.fl-faq__a {
  padding: 0 0 1.5rem;
  max-width: 60ch;
}

.fl-faq__a p {
  color: var(--clr-text);
  line-height: 1.75;
  margin-bottom: 0.875rem;
}

.fl-faq__a p:last-child { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
  .fl-faq__q,
  .fl-faq__q::after { transition: none; }
}


/* ============================================================
   Step number outside a .fb-split row
   ------------------------------------------------------------
   Steps 01 and 05 on The Process happen away from the studio, so
   they render as full-width bands rather than split rows. The
   number still needs its bottom margin trimmed there, because it
   sits above a .section-title rather than an h3.
   ============================================================ */

.fl-band .fb-split__num {
  margin-bottom: 0.25rem;
}


/* ============================================================
   BOOK NOW  ·  Guide 11 conversion page
   ------------------------------------------------------------
   Deliberately plain. Guide 11 Part 2: every extra element on a
   conversion page is a potential exit point, so this adds
   spacing and hierarchy and nothing decorative.

   Colour comes from --clr-* only. The two --pending blocks are
   dev tooling and use the same literal palette as .vp-block, per
   the documented exception.
   ============================================================ */

.fl-book__intro {
  max-width: 34rem;
  margin-bottom: 2.5rem;
}

.fl-book__lede {
  font-size: 1.0625rem;
  color: var(--clr-headings);
  margin-bottom: 0;
}

/* ---------- The action ---------- */

.fl-book__panel {
  background: var(--clr-accent-light);
  border-radius: var(--radius-sm);
  padding: 2.25rem 2rem;
  text-align: center;
  margin-bottom: 3rem;
}

.fl-book__btn {
  font-size: 1.0625rem;
  padding: 1rem 2.5rem;
}

.fl-book__panel-note {
  font-size: 0.875rem;
  color: var(--clr-muted);
  margin: 0.875rem 0 0;
  line-height: 1.6;
}

.fl-book__panel-note:first-of-type { margin-top: 1rem; }

.fl-book__panel-note code {
  font-family: Consolas, 'SFMono-Regular', Menlo, monospace;
  font-size: 0.8125rem;
  background: rgba(0, 0, 0, 0.06);
  padding: 0.1rem 0.35rem;
  border-radius: 3px;
}

/* ---------- What happens next ---------- */

.fl-book__next { margin-bottom: 2.5rem; }

.fl-book__steps {
  counter-reset: fl-book-step;
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  max-width: 40rem;
}

.fl-book__steps li {
  counter-increment: fl-book-step;
  position: relative;
  padding: 0 0 1.25rem 2.75rem;
  line-height: 1.7;
}

.fl-book__steps li:last-child { padding-bottom: 0; }

.fl-book__steps li::before {
  content: counter(fl-book-step);
  position: absolute;
  left: 0;
  top: 0.1rem;
  width: 1.75rem;
  height: 1.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--clr-accent);
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: 0.875rem;
  color: var(--clr-accent);
}

/* ---------- Reassurance ---------- */

.fl-book__reassure {
  list-style: none;
  margin: 0 0 3rem;
  padding: 1.5rem 0 0;
  border-top: 1px solid var(--clr-border);
  max-width: 40rem;
}

.fl-book__reassure li {
  position: relative;
  padding-left: 1.5rem;
  margin-bottom: 0.75rem;
  font-size: 0.9375rem;
  color: var(--clr-muted);
  line-height: 1.65;
}

.fl-book__reassure li:last-child { margin-bottom: 0; }

.fl-book__reassure li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6rem;
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: var(--clr-accent);
}

/* ---------- Alternate contact ---------- */

.fl-book__alt { max-width: 40rem; }

.fl-book__alt-details p { margin-bottom: 0.5rem; }

/* ---------- Dev-only pending states ---------- */

.fl-book__panel--pending,
.fl-book__alt--pending {
  background: #f8fafc;
  border: 1px dashed #94a3b8;
  border-radius: var(--radius-sm);
  padding: 1.5rem;
  text-align: left;
}

.fl-book__alt--pending { margin-bottom: 0; }

.fl-book__panel--pending .vp-block-label,
.fl-book__alt--pending .vp-block-label {
  display: inline-block;
  margin-bottom: 0.5rem;
}

@media (max-width: 600px) {
  .fl-book__panel { padding: 1.75rem 1.25rem; }
  .fl-book__btn { width: 100%; }
}


/* ============================================================
   CALLBACK FORM  ·  Contact.php
   ------------------------------------------------------------
   forms.css already owns .form-group, .form-control, .error and
   the fieldset/legend treatment, and it loads on every page.
   Only the gaps are here.
   ============================================================ */

.fl-form {
  max-width: 34rem;
  margin-bottom: 3rem;
}

.fl-form fieldset {
  border: none;
  padding: 0;
  margin: 0;
}

.fl-form legend {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 400;
  color: var(--clr-headings);
  padding: 0;
  margin-bottom: 1.25rem;
}

.fl-form__optional {
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--clr-muted);
  font-weight: 400;
  margin-left: 0.375rem;
}

.fl-form .btn {
  margin-top: 0.5rem;
}

@media (max-width: 600px) {
  .fl-form .btn { width: 100%; }
}


/* ============================================================
   PULLED QUOTE  ·  About page
   ------------------------------------------------------------
   base.css already styles bare <blockquote> with a left accent
   rule and italics. That is right for a quote inside body copy
   and too quiet for a quote that is the whole point of its
   section, so .fl-quote builds on it rather than replacing it.

   Deliberately a single quote treatment with no card variant.
   CLAUDE.md Step 4 names stacked identical testimonial cards on
   About pages as a pattern to avoid, so there is no grid here to
   reach for later.
   ============================================================ */

.fl-quote {
  margin: 2rem 0;
  padding: 0 0 0 1.5rem;
  border-left: 2px solid var(--clr-accent);
  font-style: normal;
  color: var(--clr-text);
  max-width: 44rem;
}

.fl-quote p {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 300;
  line-height: 1.55;
  color: var(--clr-headings);
  margin-bottom: 0.75rem;
}

.fl-quote cite {
  display: block;
  font-family: var(--font-body);
  font-style: normal;
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--clr-muted);
}

/* Larger variant, for a quote standing alone as its own section */
.fl-quote--lg {
  border-left: none;
  padding: 0;
  margin: 0 auto 1rem;
  text-align: center;
}

.fl-quote--lg p {
  font-size: clamp(1.375rem, 3vw, 1.875rem);
  line-height: 1.4;
}

@media (max-width: 600px) {
  .fl-quote { padding-left: 1rem; }
  .fl-quote p { font-size: 1.125rem; }
}


/* ============================================================
   PROCESS STEPS  ·  The Process
   ------------------------------------------------------------
   Reuses .flyer-step from pages.css rather than duplicating a
   near-identical component. That file came with the reference
   build, it loads on every page here, and it already carries the
   stacking behaviour at 820px and the vp-block positioning
   inside the media box.

   What this block changes is typography and proportion only.
   The flyer version was built for uppercase sans titles above
   bullet lists. This site sets headings in Cormorant and these
   cards hold running prose. Scoped to .fl-steps so the flyer
   pages in the reference build are untouched.

   Replaced five full-bleed rows on 2026-08-01. Three were
   .fb-split and two were .fl-band, and the .fb-split__image
   minimum height of 30rem meant the five stages of the process
   ran to roughly four screens. Guide 07 wants a sequence that
   reads as a sequence, and a sequence you have to scroll through
   in pieces is not one.
   ============================================================ */

.fl-steps {
  gap: 1.25rem;
  margin-top: 2rem;
}

.fl-steps .flyer-step {
  border-color: var(--clr-border);
  border-radius: var(--radius-sm);
}

.fl-steps .flyer-step-media {
  flex: 0 0 300px;
  min-height: 260px;
  border-radius: 0;
}

/* Flush to the corner rather than inset, and set in the display
   serif at a light weight. The flyer original is 900-weight sans
   in a rounded chip, which is right for a promotional flyer and
   loud against Cormorant. */
.fl-steps .flyer-step-num {
  top: 0;
  left: 0;
  font-family: var(--font-display);
  font-size: 1.625rem;
  font-weight: 300;
  letter-spacing: 0.03em;
  padding: 0.4rem 0.9rem 0.5rem;
  border-radius: 0;
}

.fl-steps .flyer-step-title {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  line-height: 1.25;
  color: var(--clr-headings);
  margin: 0 0 0.875rem;
}

.fl-steps .flyer-step-body {
  padding: 1.75rem 2rem;
}

.fl-steps .flyer-step-body p {
  color: var(--clr-muted);
  font-size: 0.9375rem;
  line-height: 1.7;
  margin: 0 0 0.75rem;
}

/* The one line in each card that names what the visitor has to
   do. Ruled off so the five cards can be skimmed for those five
   lines alone, which is how a first-timer reads this page. */
.fl-steps .fl-step__yours {
  margin: auto 0 0 !important;
  padding-top: 0.875rem;
  border-top: 1px solid var(--clr-border);
  color: var(--clr-text) !important;
}

@media (max-width: 820px) {
  .fl-steps .flyer-step-media {
    height: 220px;
    min-height: 0;
  }
  .fl-steps .flyer-step-body { padding: 1.5rem; }
  .fl-steps .flyer-step-title { font-size: 1.375rem; }
}


/* ============================================================
   SERVICE OVERVIEW LIST  ·  Home
   ------------------------------------------------------------
   A hairline-separated list with small landscape thumbnails.
   Replaced three full-bleed .fb-split rows on 2026-08-01, which
   between them put around 1,800px of scrolling into the middle
   of the homepage for three sentences of copy.

   Deliberately NOT the .flyer-step card used on The Process.
   That component is a stack of bordered cards because a numbered
   sequence needs to look like one, and the whole reason this
   block was rebuilt is that it had been reading as a sequence
   when it is a set of three routes. A ruled list reads as a
   list. Keeping the two treatments apart is the point, so if
   this is ever merged with .fl-steps, that distinction goes with
   it.

   Three items with matching structure is the pattern CLAUDE.md
   Step 4 warns about. The hairline rules rather than boxes, the
   uneven copy lengths and the absence of numbering are what keep
   it reading as prose with pictures rather than a card wall.
   ============================================================ */

.fl-offer {
  margin-top: 2.5rem;
  border-top: 1px solid var(--clr-border);
}

.fl-offer__item {
  display: flex;
  align-items: center;
  gap: 1.75rem;
  padding: 1.75rem 0;
  border-bottom: 1px solid var(--clr-border);
}

.fl-offer__media {
  flex: 0 0 200px;
  height: 140px;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--clr-light);
}

.fl-offer__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.fl-offer__body {
  flex: 1 1 auto;
  min-width: 0;
}

.fl-offer__title {
  font-family: var(--font-display);
  font-size: 1.375rem;
  font-weight: 400;
  line-height: 1.25;
  color: var(--clr-headings);
  margin: 0 0 0.5rem;
}

.fl-offer__body p {
  color: var(--clr-muted);
  font-size: 0.9375rem;
  line-height: 1.7;
  margin: 0 0 0.625rem;
}

@media (max-width: 700px) {
  .fl-offer__item {
    flex-direction: column;
    align-items: flex-start;
    gap: 1.125rem;
  }
  .fl-offer__media {
    flex: 0 0 auto;
    width: 100%;
    height: 190px;
  }
  .fl-offer__title { font-size: 1.25rem; }
}


/* ============================================================
   FLYER LAYOUT  ·  The Process
   ------------------------------------------------------------
   The Process was rebuilt on 2026-08-01 to the shape of
   examplecorp-claude/Flyer-Steps-Starter-Package.php: a
   full-width image band, then the intro underneath it, then the
   numbers, then the step cards. No split rows anywhere.

   Every component comes from css/pages.css, which is loaded on
   every page of this site. This block retunes three of them for
   Cormorant and the studio palette, and nothing here changes the
   originals, so the reference build's flyer pages are untouched.
   ============================================================ */

/* ---------- Hero band ---------- */

/* 365px is the flyer default and it reads as a strip rather than
   a hero against this site's type scale, where the H1 underneath
   it can reach 44px. */
.fl-hero {
  height: 420px;
}

/* ---------- Intro band ---------- */

.fl-intro {
  padding: 3rem 1.5rem 3.25rem;
}

.fl-intro h1 {
  margin-bottom: 1.5rem;
}

/* The flyer callout is a bordered lead paragraph. Sized up a
   little and lightened, because this one carries the sentence
   that does the emotional work on the page and it should not
   look like a sidebar note. */
.fl-intro .flyer-callout {
  font-size: 1.125rem;
  font-weight: 300;
  line-height: 1.7;
  padding: 1.25rem 1.5rem;
  border-left-width: 2px;
  border-radius: 0;
}

.fl-intro > p {
  color: var(--clr-muted);
  margin-bottom: 1rem;
}

/* ---------- Stats band ---------- */

/* The flyer sets these at 800 weight in the UI sans, which is
   right for a promotional flyer shouting a price. Here the four
   numbers are durations, and their job is reassurance rather
   than emphasis, so they are set in the display serif at the
   same light weight as the headings. */
.fl-stats .flyer-stat {
  padding: 1.75rem 1.25rem;
}

.fl-stats .flyer-stat__num {
  font-family: var(--font-display);
  font-size: 2.25rem;
  font-weight: 300;
  letter-spacing: 0.01em;
  color: var(--clr-accent-on-dark);
  margin-bottom: 0.5rem;
}

/* pages.css hardcodes #999 here. That was picked against the
   reference build's palette, not this one. */
.fl-stats .flyer-stat__label {
  color: rgba(255, 255, 255, 0.62);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
}

@media (max-width: 820px) {
  .fl-hero { height: 300px; }
  .fl-intro { padding: 2.25rem 1.25rem 2.5rem; }
  .fl-intro .flyer-callout { font-size: 1.0625rem; padding: 1.125rem 1.25rem; }
}

@media (max-width: 600px) {
  .fl-hero { height: 240px; }
  .fl-stats .flyer-stat__num { font-size: 1.875rem; }
}


/* ============================================================
   CONTAINED FIGURE  ·  Pricing
   ------------------------------------------------------------
   05-article-resource.md Part 3 Section 1 ends its visual brief
   with "Never full-bleed — this is an article, not a landing
   page." So the one image on Pricing sits inside the text
   column at a fixed 3:2 ratio rather than in a .fb-split or a
   .flyer-hero band.

   The ratio box exists so the .vp-block has a sized parent to
   fill, per _01-visual-psychology.md Part 5A.1: the placeholder
   occupies the slot the real photo will occupy, rather than
   sitting beside a collapsed empty one.
   ============================================================ */

.fl-pricing-figure {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 2;
  margin: 2.25rem 0 0.5rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--clr-light);
}

.fl-pricing-figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.fl-pricing-figure .vp-block {
  position: absolute;
  inset: 0;
  border-radius: 0;
}

/* Price tables run three columns and the middle one is the figure.
   Give it room so a range never wraps mid-number.

   SCOPED TO --price, and that modifier is not decoration.

   Written 2026-08-02 without it, as a bare `.art-timeline
   td:nth-child(2)`. `.art-timeline` is a shared class from
   pages.css and three pages use it. On Pricing, column two holds
   "$300-700" and nowrap is right. On Which Method and Extensions
   for Fine Hair, column two holds a full sentence, and nowrap put
   that sentence on one unbreakable line, which pushed the whole
   table roughly 500px past its .container-narrow. Spotted on
   screen 2026-08-05, not by any check here.

   The lesson is the one already in the notes about naming: adding
   a rule to a shared class changes every page that uses it, not
   the page you were looking at. Scope it. */
.art-timeline--price td:nth-child(2) {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 560px) {
  .art-timeline--price td:nth-child(2) { white-space: normal; }
}

/* Belt and braces. Deliberately NOT table-layout: fixed — pages.css
   sets the 38% first-column width on `td`, not `th`, and fixed
   layout reads its widths from the first row, which is the thead.
   Switching to fixed would quietly re-proportion every one of these
   tables to equal columns. These two properties are enough: with
   the nowrap gone, ordinary prose wraps and the table stays inside
   its container. */
.art-timeline {
  max-width: 100%;
}

.art-timeline td,
.art-timeline th {
  overflow-wrap: break-word;
}


/* ============================================================
   SELF-ASSESSMENT  ·  Which Method Is Right for Me?
   ------------------------------------------------------------
   10-diagnostic-audit.md Part 3 Section 2 asks for 8-12
   observable items grouped into 3-4 categories. This is the
   form that holds them.

   Deliberately not using the shared .form-control / .form-group
   pattern from forms.css. That styles a submitting form, and
   this one submits nothing and stores nothing. Making it look
   like the callback form would imply the answers go somewhere.
   ============================================================ */

.fl-check {
  margin-top: 2rem;
}

.fl-check__group {
  border: none;
  border-top: 1px solid var(--clr-border);
  margin: 0;
  padding: 1.5rem 0 0.5rem;
}

.fl-check__group legend {
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--clr-accent);
  padding: 0 0.75rem 0 0;
}

.fl-check__item {
  display: flex;
  align-items: flex-start;
  gap: 0.875rem;
  padding: 0.625rem 0;
  cursor: pointer;
  line-height: 1.6;
  color: var(--clr-text);
}

.fl-check__item input {
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0.25rem 0 0;
  accent-color: var(--clr-accent);
  cursor: pointer;
}

.fl-check__item:hover span {
  color: var(--clr-headings);
}

/* The running total. Hidden until at least one box is ticked,
   and it never appears at all without JavaScript. */
.fl-check__count {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 300;
  color: var(--clr-headings);
  margin: 0 0 1.5rem;
}

/* ---------- The three result bands ---------- */

/* All three are fully legible by default. The script dims the two
   that do not apply rather than hiding them, so the page still
   answers the question for a reader with no JavaScript and for
   anyone who wants to read all three. */
.fl-band-list__item {
  border-left: 2px solid var(--clr-accent);
  padding: 0.25rem 0 0.25rem 1.5rem;
  margin-bottom: 2rem;
  transition: opacity 0.25s ease, border-color 0.25s ease;
}

.fl-band-list__item h3 {
  font-size: 1.25rem;
  font-weight: 400;
  margin: 0 0 0.5rem;
}

.fl-band-list__item p {
  color: var(--clr-muted);
  margin-bottom: 0.75rem;
}

.fl-band-list__item p:last-child {
  margin-bottom: 0;
}

.fl-band-list__item.is-muted {
  opacity: 0.42;
  border-left-color: var(--clr-border);
}

@media (max-width: 600px) {
  .fl-band-list__item { padding-left: 1rem; }
  .fl-check__item { gap: 0.75rem; }
}


/* ============================================================
   BUILD FLAG  ·  [PRICING NEEDED] and friends
   ------------------------------------------------------------
   Added 2026-08-04. A visible, deliberately ugly marker for a
   gap that must be closed before go-live but must not stop the
   build. CLAUDE.md Step 3.8: placeholder it, flag it, log it,
   keep going.

   Same amber as .vp-block on purpose. If it looks like copy it
   has failed at its only job, which is to be impossible to miss
   and impossible to mistake for something a client should read.

   GO-LIVE CLEANUP: grep the site for "PRICING NEEDED", clear
   every hit, then delete this rule. If the rule is still here
   the site is not ready.
   ============================================================ */

.fl-flag {
  display: inline-block;
  font-family: Consolas, 'SFMono-Regular', Menlo, monospace;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1.4;
  white-space: nowrap;
  color: #7a4b00;
  background: #fff4d6;
  border: 1px dashed #d69b1e;
  border-radius: 3px;
  padding: 0.125rem 0.4rem;
  margin-left: 0.375rem;
  vertical-align: baseline;
}


/* ============================================================
   GALLERY GRID  ·  Cloudinary-fed
   ------------------------------------------------------------
   Added 2026-08-05. Masonry-ish without a masonry library: a
   plain responsive grid of 3:4 portrait tiles. Portrait because
   the subject is long hair, per _info/_visual-direction.md.

   Sizing is done by Cloudinary, not here — the src already
   arrives at 700x930 and correctly rotated. This file only
   lays them out.
   ============================================================ */

.fl-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1rem;
  margin-top: 1rem;
}

.fl-gallery__item {
  display: block;
  position: relative;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--clr-light);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.fl-gallery__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.fl-gallery__item:hover,
.fl-gallery__item:focus-visible {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

/* The lift is decorative. Anyone who has asked the OS to stop
   animating things should not get it. */
@media (prefers-reduced-motion: reduce) {
  .fl-gallery__item { transition: none; }
  .fl-gallery__item:hover,
  .fl-gallery__item:focus-visible { transform: none; }
}

@media (max-width: 600px) {
  .fl-gallery { grid-template-columns: repeat(2, 1fr); gap: 0.625rem; }
}


/* ── .fl-answer · quotable answer block, light treatment ──────────
   23-aio-guide.md Part 16.1 wants a short self-contained answer as
   the first paragraph after an h2, written to survive being quoted
   on its own. pages.css already has .art-callout for that job and it
   is a solid dark box on --clr-dark.

   One of those on a page is a feature. Five is a wall, and it runs
   into the CLAUDE.md Step 4 rule about stacking identical blocks.
   Maintenance.php needs five answer blocks, so it keeps ONE
   .art-callout for the primary answer near the top of the page and
   uses this for the rest. Pricing.php has two and is fine as it is.

   Deliberately no background and no colour declaration. The text
   inherits --clr-text from body. This is emphasis on a paragraph,
   not a component with a palette of its own, and adding a colour
   here would be the exact "colour on a structural class" mistake
   the COLOUR CONTROL rules warn about. */
.fl-answer {
  border-left: 2px solid var(--clr-accent);
  padding-left: 1.375rem;
  margin: 1.75rem 0 2rem;
}

.fl-answer p {
  font-size: 1.125rem;
  line-height: 1.68;
  margin-bottom: 0;
}

@media (max-width: 600px) {
  .fl-answer { padding-left: 1rem; }
  .fl-answer p { font-size: 1.0625rem; }
}


/* ── Contact page · two-column layout and the full-width map ──────
   2026-08-06. Contact.php moved from one narrow column onto the
   .contact-grid the reference build uses, so the container widened
   from container-narrow to container. These rules handle the two
   consequences of that.

   .contact-grid, .contact-info-block and .contact-detail-* all come
   from css/forms.css and are at reference-template parity. Nothing
   here overrides them, it only fills the gaps they do not cover. */

/* INTRO BAND. Uses .container rather than .flyer-intro so its left
   edge lines up with the grid below, see the note in Contact.php.
   The prose is capped on its own so a 1232px container does not
   produce a 145-character measure. */
.fl-contact-intro {
  padding-top: 3rem;
  padding-bottom: 2.5rem;
}

.fl-contact-intro .flyer-callout,
.fl-contact-intro > p {
  max-width: 46rem;
}

.fl-contact-intro > p:last-child {
  margin-bottom: 0;
}

/* Same job as .fl-gallery-section: the band above is white and so is
   this section, so the full section padding was separating nothing.
   Kept as its own class rather than shared, because the two pages
   may want different numbers later. */
.section.fl-contact-section {
  padding-top: 2.5rem;
}

/* The reassurance list lives inside the form column now, so it needs
   separating from the submit button rather than from a section edge. */
.fl-contact-form-col .fl-book__reassure {
  margin-top: 2rem;
}

/* THE ASIDE PANEL. Fills the void left when the info column runs out
   before the form does. --clr-base rather than a white card, because
   the section behind it is already white and a white card on white
   needs a border to exist at all.

   NO COLOUR SET ON THE TEXT. base.css owns headings and body colour,
   and --clr-base is a pale tint that the default text reads fine on. */
.fl-contact-aside {
  background: var(--clr-base);
  border-radius: var(--radius-md);
  padding: 2rem 1.75rem;
}

.fl-contact-aside__title {
  font-size: 1.375rem;
  margin-bottom: 0.875rem;
}

/* .contact-detail-item in forms.css draws a hairline under each row.
   Inside a filled panel the last one sits above the padding and reads
   as a stray line, so it comes off. */
.fl-contact-aside .contact-detail-item:last-of-type {
  border-bottom: none;
  padding-bottom: 0;
  margin-bottom: 0;
}

@media (max-width: 900px) {
  .fl-contact-aside {
    padding: 1.5rem 1.25rem;
  }
}

/* FULL-WIDTH MAP ROW.
   Sits outside any container on purpose. Height lives here rather
   than on the iframe so it can come down on small screens, where a
   360px-tall map is most of the fold.

   display:block on the iframe kills the inline-element descender
   gap, which otherwise shows as a few pixels of background under
   the map and reads as a misalignment. */
.fl-map {
  width: 100%;
  line-height: 0;
  background: var(--clr-base);
}

.fl-map iframe {
  display: block;
  width: 100%;
  height: 360px;
  border: 0;
}

/* line-height is reset here because .fl-map zeroes it to kill the iframe
   descender gap, and the caption is real prose that needs it back. */
.fl-map__caption {
  line-height: 1.6;
  max-width: 46rem;
  margin: 0 auto;
  padding: 1.125rem 1.5rem 0;
  font-size: 0.9375rem;
  text-align: center;
}

@media (max-width: 700px) {
  .fl-map iframe { height: 260px; }
}

/* ── .fl-gallery-section · closes the gap under the intro band ────
   2026-08-06. There was about 175px of empty white between the intro
   paragraph and the first "Machine Weft" heading, and it read as a
   hole rather than as breathing room.

   Three things were stacking, and none of them was wrong on its own:
   .flyer-intro's 2.5rem bottom padding, the section's 7rem of
   section-xl top padding, and the paragraph's own bottom margin.
   The first heading was already at margin-top: 0 via :first-of-type
   below, so that was not the cause.

   Fixed in two moves. Gallery.php dropped section-xl for section,
   and this trims the top padding further. It is safe to go this
   tight here ONLY because the intro band and this section are both
   white, so there is no colour change for the padding to sit either
   side of. Do not copy this onto a section that changes background.

   Bottom padding is deliberately left alone. The disclaimer
   paragraph sits at the foot of this section and the next section
   is a different colour, so that edge is a real boundary. */
.section.fl-gallery-section {
  padding-top: 2.5rem;
}

/* ── .fl-gallery__heading · grouped gallery sections ──────────────
   Added 2026-08-06. Gallery.php now renders one h2 and one grid per
   Cloudinary folder instead of a single flat grid.

   NO COLOUR DECLARATION HERE ON PURPOSE. base.css owns the h1-h6
   colour for the whole site, and setting it again on a structural
   class is the mistake the COLOUR CONTROL rules describe. It has
   already cost this build one white-heading bug.

   scroll-margin-top exists because the headings carry ids and the
   header is fixed. Without it, jumping to #machine-weft puts the
   heading underneath the header. */
.fl-gallery__heading {
  margin: 3rem 0 1.25rem;
  font-size: 1.5rem;
  scroll-margin-top: 6rem;
}

.fl-gallery__heading:first-of-type {
  margin-top: 0;
}

/* Space between one grid and the next heading. Set on the pair
   rather than as a blanket margin so a single-group gallery does
   not gain a trailing gap. */
.fl-gallery + .fl-gallery__heading {
  margin-top: 3.5rem;
}

@media (max-width: 600px) {
  .fl-gallery__heading { margin: 2.25rem 0 1rem; font-size: 1.3rem; }
  .fl-gallery + .fl-gallery__heading { margin-top: 2.5rem; }
}
