
  /* =========================================================================
     APEX TEMPLATE CHROME
     Every rule is prefixed with [data-site-template="apex"]. This block is only
     injected for a shop that resolves to Apex, and even if it were injected for
     a classic shop, not one selector would match.
     ========================================================================= */

  /* ---- Content column ----
     Apex runs a tighter column than classic. --site-content-max is read by
     .site-wrap and by the handful of section components that used to hardcode
     the same number, so one value moves them all together. */
  [data-site-template="apex"] { --site-content-max: 1424px; }
  /* .site-wrap is a flex ITEM inside the Apex hero (the band is flex so the copy
     can sit on the bottom edge). Without an explicit width a flex item shrinks to
     its content, which pulled the hero copy 138px in from where every other
     band's content starts. Filling the line makes the hero align with the rest of
     the page, and max-width still caps the column. */
  [data-site-template="apex"] .site-wrap { width: 100%; }

  /* ---- No sideways scroll ----
     The rotated marquee is over-scaled on purpose so its ends run off both
     edges, which put the page 36px wider than the viewport and produced a real
     horizontal scrollbar at every width. Clipping the page body is how the
     design handles it. Clip rather than hidden so no scroll container is
     created, and it is scoped to main rather than the site root because the
     nav is sticky and the mobile nav panel is fixed; both are siblings of main,
     and the section gear popovers are portalled to the body, so nothing that
     needs to escape lives inside this box. */
  [data-site-template="apex"] main { overflow-x: clip; }

  /* ---- Kill the classic film grain ----
     Higher specificity than the base .site-root::before rule (attribute + class
     beats class), so the overlay is removed rather than the base rule edited.
     Apex has no texture of any kind. */
  [data-site-template="apex"].site-root::before { content: none; }

  /* ---- Display type ----
     Upright condensed uppercase, at EVERY heading level. The signed-off design
     sets one display treatment and uses it from the hero headline all the way
     down to a film name and a process step title; leaving h3 and below in
     sentence case at weight 800 was what made the sub-headings read as ordinary
     bold body copy and flattened the whole page. Only the SIZE changes by level
     (the section components own that), never the face, the case or the weight. */
  [data-site-template="apex"] h1,
  [data-site-template="apex"] h2,
  [data-site-template="apex"] h3,
  [data-site-template="apex"] h4,
  [data-site-template="apex"] h5,
  [data-site-template="apex"] h6 {
    /* The condensed uppercase display treatment is the design's signature, and
       it is also the single loudest thing on the page, so a quiet look has to be
       able to put it back to the typeface's own case and weight. */
    text-transform: var(--orn-head-case, uppercase);
    font-weight: var(--orn-head-weight, 900);
    letter-spacing: var(--orn-head-track, -0.004em);
    font-variation-settings: 'wdth' var(--orn-head-width, 92);
  }
  [data-site-template="apex"] h1,
  [data-site-template="apex"] h2 {
    line-height: 0.96;
    text-wrap: balance;
  }
  /* Sub-heading levels need a little more leading than the display levels,
     because they wrap inside a card or a narrow column rather than across a
     full-width band. */
  [data-site-template="apex"] h3,
  [data-site-template="apex"] h4,
  [data-site-template="apex"] h5,
  [data-site-template="apex"] h6 {
    line-height: 1.08;
  }
  /* One phrase in the accent colour. Section components mark an emphasised
     span with .site-accent-text where they have one. */
  [data-site-template="apex"] .site-accent-text { color: var(--site-accent); }

  /* ---- Buttons: pill + circular arrow badge ----
     Redefines the shared .site-btn used by every CTA on every page. The badge
     is the existing .site-btn-arrow / .site-nav-cta-arrow span, restyled into a
     circle; it rotates 45deg on hover. clip-path is cleared because the classic
     look (and any saved angular shaping) would cut the pill.
     NOTE: a shop's saved Shaping setting deliberately still applies to CARDS
     (.site-cut / .site-cut-lg) per the carry-over rule. Only the button itself
     is pinned to a pill, because a pill IS the Apex button. */
  [data-site-template="apex"] .site-btn {
    clip-path: none;
    border-radius: 999px;
    gap: 12px;
    padding: 7px 7px 7px 24px;
    font-weight: 800;
    letter-spacing: 0.05em;
    font-size: 13px;
    border-width: 1.5px;
    transition: transform .3s cubic-bezier(.2,.7,.3,1), background .3s ease, border-color .3s ease, color .3s ease;
  }
  [data-site-template="apex"] .site-btn:hover { transform: translateY(-2px); box-shadow: none; }
  [data-site-template="apex"] .site-btn-arrow,
  [data-site-template="apex"] .site-nav-cta-arrow {
    /* The circular badge on the end of every button. Off leaves a plain pill. */
    display: var(--orn-btn-arrow, inline-grid);
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 999px;
    flex: none;
    font-size: 14px;
    line-height: 1;
    background: var(--site-fg);
    color: var(--site-bg);
    transition: transform .4s cubic-bezier(.2,.7,.3,1);
  }
  [data-site-template="apex"] .site-btn:hover .site-btn-arrow,
  [data-site-template="apex"] .site-btn:hover .site-nav-cta-arrow {
    transform: rotate(45deg);
  }
  /* On the accent-filled primary button the badge inverts to the button's own
     label colour so it reads on any shop accent. */
  [data-site-template="apex"] .site-btn-primary .site-btn-arrow,
  [data-site-template="apex"] .site-btn-primary .site-nav-cta-arrow {
    background: var(--site-btn-fg, #fff);
    color: var(--site-accent);
  }
  /* The second button in a pair. It used to be an outline round nothing, and
     with the outlines off it was bare wording that did not read as a button at
     all. A soft wash of the page's own ink gives it a shape without a line. */
  [data-site-template="apex"] .site-btn-ghost {
    background: var(--site-btn-soft);
    border-color: transparent;
  }
  [data-site-template="apex"] .site-btn-ghost .site-btn-arrow {
    background: var(--site-accent);
    color: var(--site-btn-fg, #fff);
  }
  [data-site-template="apex"] .site-btn-ghost:hover { background: var(--site-btn-soft-hover); }

  /* ---- Eyebrow: the marker bar ----
     An accent bar that fades out to the right with letterspaced uppercase text
     sitting on it. The classic triangle and trailing // are cleared.
     A shop that saved an Accents style (brackets / precision / sport) keeps its
     accent mark on top: those rules are written at a higher specificity and are
     intentionally left to win, because saved Site Style carries over to Apex. */
  [data-site-template="apex"] .site-eyebrow {
    /* Three treatments: the accent bar the design ships with, the same words
       with no bar, or no label at all. */
    display: var(--orn-eyebrow-display, inline-block);
    padding: var(--orn-eyebrow-pad, 8px 44px 8px 15px);
    /* Solid accent behind the TEXT, fading out across the trailing padding
       only. Fading from the middle of the label (which is what the mockup does
       over a dark band) turns the second half of the word invisible the moment
       the same bar sits on a light band, so the fade is pinned to the padding
       instead and the label reads on any band. */
    background: var(--orn-eyebrow-bg, linear-gradient(
      90deg,
      var(--site-accent) 0%,
      var(--site-accent) calc(100% - 44px),
      transparent 100%
    ));
    color: var(--orn-eyebrow-fg, var(--site-btn-fg, #fff));
    font-weight: 600;
    font-size: 11px;
    letter-spacing: 0.3em;
    line-height: 1.5;
    margin-bottom: 4px;
  }
  [data-site-template="apex"] .site-eyebrow:not(:empty)::before,
  [data-site-template="apex"] .site-eyebrow:not(:empty)::after { content: none; }
  [data-site-template="apex"] .site-eyebrow.center { display: var(--orn-eyebrow-display, inline-block); }

  /* ---- Card shape ----
     Apex's own card shape is a soft rounded rectangle, used when the shop has
     NOT saved a Shaping setting. A shop that has saved one keeps it: those
     rules carry two attribute selectors and therefore outrank this one, which
     is the saved-style carry-over rule expressed in the cascade rather than in
     a branch. The classic bottom-left notch is Classic's signature, not Apex's,
     so an untouched shop should not inherit it. */
  [data-site-template="apex"] .site-cut,
  [data-site-template="apex"] .site-cut-lg {
    clip-path: none;
    border-radius: 16px;
  }

  /* ---- Classic corner brackets ----
     .site-brk paints an accent bracket into two corners of a card by default.
     That is Classic chrome rather than anything the shop chose, so Apex clears
     it. A shop that HAS chosen an Accents style still gets its marks: those are
     drawn on the separate .site-acc layer, which this does not touch. */
  [data-site-template="apex"] .site-brk::before,
  [data-site-template="apex"] .site-brk::after { content: none; }

  /* ---- Section rhythm ----
     Tighter, more deliberate bands than classic's 104px. The two values come
     through custom properties so a BAND can adjust its own rhythm without the
     padding declaration itself gaining specificity, which is what keeps the
     shop's Spacing setting able to override it (see the block below).

     A light band carries the design's asymmetry: a touch more room on top,
     a touch less underneath, because its top edge is where the eye enters the
     band and its bottom edge runs straight into the next one. */
  [data-site-template="apex"] {
    --ax-sec-pad-t: clamp(80px, 7.6vw, 118px);
    --ax-sec-pad-b: clamp(80px, 7.6vw, 118px);
  }
  [data-site-template="apex"] [data-sec-band="light"] {
    --ax-sec-pad-t: clamp(84px, 8vw, 124px);
    --ax-sec-pad-b: clamp(76px, 7vw, 110px);
  }
  [data-site-template="apex"] .site-sec {
    padding: var(--ax-sec-pad-t, clamp(80px, 7.6vw, 118px)) 0 var(--ax-sec-pad-b, clamp(80px, 7.6vw, 118px));
  }
  @media (max-width: 900px) {
    [data-site-template="apex"],
    [data-site-template="apex"] [data-sec-band="light"] { --ax-sec-pad-t: 68px; --ax-sec-pad-b: 68px; }
  }
  @media (max-width: 600px) {
    [data-site-template="apex"],
    [data-site-template="apex"] [data-sec-band="light"] { --ax-sec-pad-t: 52px; --ax-sec-pad-b: 52px; }
  }

  /* ---- Keep the shop's Spacing setting working ----
     APEX_CSS is injected AFTER the base [data-sec-spacing] rules, so the Apex
     padding above was silently beating them at equal specificity and the
     per-section Spacing control (and the sitewide Spacing default) did nothing on
     an Apex page. Re-asserting them here, scoped to the template, puts them back
     in front. Values match the base block exactly. */
  /* THE DOUBLED CLASS IS DELIBERATE, do not "tidy" it away. Re-asserting these
     rules once was not enough. A handful of Apex bands set their own padding
     with a two class selector of their own (the closing band is
     "ax-cta site-sec"), which ties with the single class version here and then
     wins on source order, because the home stylesheet is injected after this
     one. Writing the class twice costs nothing, changes no colour or size, and
     puts the shop's own Spacing choice back in front of every one of them.
     Measured: the closing band sat at 106.56px whether the shop chose Compact
     or Roomy, and now moves between 64px and 148px like every other band. */
  [data-site-template="apex"] [data-sec-spacing="compact"] .site-sec.site-sec { padding-top: 64px; padding-bottom: 64px; }
  [data-site-template="apex"] [data-sec-spacing="normal"] .site-sec.site-sec { padding-top: 104px; padding-bottom: 104px; }
  [data-site-template="apex"] [data-sec-spacing="roomy"] .site-sec.site-sec { padding-top: 148px; padding-bottom: 148px; }
  @media (max-width: 900px) {
    [data-site-template="apex"] [data-sec-spacing="compact"] .site-sec.site-sec { padding-top: 48px; padding-bottom: 48px; }
    [data-site-template="apex"] [data-sec-spacing="normal"] .site-sec.site-sec { padding-top: 76px; padding-bottom: 76px; }
    [data-site-template="apex"] [data-sec-spacing="roomy"] .site-sec.site-sec { padding-top: 104px; padding-bottom: 104px; }
  }

  /* ---- Section band surface ----
     data-sec-band re-points the colour tokens for one band only. Because every
     section component reads --site-bg / --site-fg / --site-muted / --site-border
     rather than literal colours, flipping these on the band flips the whole
     section. This is the mechanism the alternating dark/light page rhythm is
     built from; which sections get which band is data (APEX_SECTION_BANDS). */
  [data-site-template="apex"] [data-sec-band] .site-sec {
    background: var(--site-bg);
    color: var(--site-fg);
  }
  [data-site-template="apex"] [data-sec-band="light"] .site-sec {
    /* The shop's own light ground, falling back to the design's white. The
       second tone is derived from it in siteStyleTokens rather than guessed
       here, because CSS cannot lighten a colour it was handed. */
    --site-bg: var(--site-band-light, #ffffff);
    --site-bg-2: var(--site-band-light-2, #edeff2);
    --site-fg: #0a0d12;
    --site-surface: #edeff2;
    --site-surface-2: #e3e6ea;
    --site-panel: #ffffff;
    --site-card-bg: #ffffff;
    --site-border: rgba(10,13,18,0.14);
    --site-muted: #4b5563;
    --site-subtle: #6b7280;
    --site-primary: #0a0d12;
    --site-shadow: 0 1px 2px rgba(10,13,18,.05),0 30px 60px -26px rgba(10,13,18,.28);
  }
  [data-site-template="apex"] [data-sec-band="dark"] .site-sec {
    --site-bg: var(--site-band-dark, #07080a);
    --site-bg-2: var(--site-band-dark-2, #0c0e12);
    --site-fg: #ffffff;
    --site-surface: #12151a;
    --site-surface-2: #171b21;
    --site-panel: #12151a;
    --site-card-bg: #12151a;
    --site-border: rgba(255,255,255,0.12);
    --site-muted: rgba(255,255,255,0.64);
    --site-subtle: rgba(255,255,255,0.42);
    --site-primary: #ffffff;
    --site-shadow: 0 1px 2px rgba(0,0,0,.5),0 34px 62px -24px rgba(0,0,0,.75);
  }

  /* ---- The tonal step ----
     Tone 2 lifts a band one step off its neighbours, which is the whole reason
     the page reads as a rhythm rather than one long slab. Before this, three
     consecutive dark bands were the identical #07080a with no seam between them,
     and a white card on a white light band had nothing but a hairline border to
     sit against. Only --site-bg moves; every other token stays, so contrast and
     card colours are unchanged and nothing has to be re-checked for readability.
     Which sections get tone 2 is data (APEX_SECTION_TONES). */
  /* Tone 2 is the lift that stops two neighbouring bands of the same family
     being the identical colour. It is what keeps a page committed to one family
     from reading as a single slab, so it holds in all three band modes. */
  [data-site-template="apex"] [data-sec-band="dark"][data-sec-tone="2"] .site-sec { --site-bg: var(--site-band-dark-2, #0c0e12); }
  [data-site-template="apex"] [data-sec-band="light"][data-sec-tone="2"] .site-sec { --site-bg: var(--site-band-light-2, #edeff2); }

  /* ---- The angled band cut ----
     A diagonal slice off the top of a band, so two neighbouring bands meet on a
     slant instead of a straight line. Opt-in per section via data-sec-cut, so
     it can never clip a section that was not designed for it. The cut depth is
     well inside the band's own top padding. */
  [data-site-template="apex"] [data-sec-cut="top"] .site-sec {
    clip-path: polygon(0 44px, 100% 0, 100% 100%, 0 100%);
  }
  [data-site-template="apex"] [data-sec-cut="bottom"] .site-sec {
    clip-path: polygon(0 0, 100% 0, 100% calc(100% - 44px), 0 100%);
  }
  @media (max-width: 600px) {
    [data-site-template="apex"] [data-sec-cut="top"] .site-sec { clip-path: polygon(0 22px, 100% 0, 100% 100%, 0 100%); }
    [data-site-template="apex"] [data-sec-cut="bottom"] .site-sec { clip-path: polygon(0 0, 100% 0, 100% calc(100% - 22px), 0 100%); }
  }

  /* =========================================================================
     BACKDROPS
     Exactly four options, and NEVER a repeating pattern: a dimmed full width
     photo, a soft accent glow that slowly drifts, a giant ghosted word or logo
     mark, or nothing. Painted on the section's own pseudo-elements, so no
     section component has to change and any section can carry one.

     .site-sec is already position:relative. Its in-flow children are lifted to
     z-index 1 (the same trick the existing .site-glow rule uses) so the
     backdrop sits behind the content and above the band background.
     ========================================================================= */
  [data-site-template="apex"] [data-sec-backdrop]:not([data-sec-backdrop="none"]) .site-sec {
    isolation: isolate;
    /* A glow drifts past the band edge and a mark hangs off a corner on purpose.
       Clipping to the band keeps both inside their own section and means neither
       can ever produce a horizontal scrollbar on a narrow screen. */
    overflow: hidden;
  }
  [data-site-template="apex"] [data-sec-backdrop]:not([data-sec-backdrop="none"]) .site-sec > * {
    position: relative;
    z-index: 1;
  }
  /* The full-bleed backdrops (photo, glow) fill the band. MARK is excluded here
     and carries its own complete block below, because it is the one backdrop that
     is NOT full bleed: it hangs off a corner and sizes to itself. Excluding it
     rather than trying to out-specify this rule is deliberate. This selector
     carries four compound parts and the mark rules carry three, so a mark could
     never override the content or the inset from here no matter what it declared,
     and the ghost word silently rendered as an empty string. */
  [data-site-template="apex"] [data-sec-backdrop]:not([data-sec-backdrop="none"]):not([data-sec-backdrop="mark"]) .site-sec::before,
  [data-site-template="apex"] [data-sec-backdrop="photo"] .site-sec::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    /* THESE TWO LINES ARE LORE. Sections also carry the older site-glow class,
       whose own ::before rule is top:0, right:0, width:40%, height:60%, a small
       top-right edge bloom from before Apex existed. It targets THE SAME
       pseudo-element as this rule, so the two merge.

       With inset:0 setting left AND right, and site-glow setting an explicit
       width, CSS drops right and anchors to the left. The backdrop stopped
       filling the band and became a 40% by 60% box pinned to the LEFT, with this
       layer's gradient drawn inside it. On a 1440 wide band that is a 576 by 561
       rectangle sitting mid-page with hard edges, which is what Joey reported on
       2026-08-25: "the gradient or whatever is on looks weird." It was on every
       Apex page carrying a glow, the signed-off auto tint page included.

       Resetting both to auto lets inset:0 resolve on its own, which is what this
       rule always meant. site-glow is left alone, so it still behaves exactly as
       before under the classic template, where none of these rules apply. */
    width: auto;
    height: auto;
  }

  /* ---- Backdrop: dimmed full width photo ----
     The photo sits at a low opacity and a veil fades the band's own background
     back in at the top and bottom edges, so the band never ends on a hard
     photo seam. */
  [data-site-template="apex"] [data-sec-backdrop="photo"] .site-sec::before {
    background-image: var(--sec-backdrop-image);
    background-size: cover;
    background-position: center;
    opacity: var(--sec-backdrop-opacity, 0.18);
  }
  [data-site-template="apex"] [data-sec-backdrop="photo"] .site-sec::after {
    background: linear-gradient(
      to bottom,
      var(--site-bg) 0%,
      transparent 22%,
      transparent 78%,
      var(--site-bg) 100%
    );
  }

  /* ---- Backdrop: soft accent glow ----
     A radial bloom in the shop's accent, drifting slowly. No texture at all.
     Honours prefers-reduced-motion (see the block at the end). */
  [data-site-template="apex"] [data-sec-backdrop="glow"] .site-sec::before {
    /* Split out of the old single backdrops switch. That one word turned off the
       ghosted words AND the glows together, so a look could not keep a lit page
       and lose the giant words. It still silences both when it is off, because
       two shipped themes store it and their look must not move; this only
       refines it. */
    display: var(--orn-band-glow, block);
    background: radial-gradient(
      44% 48% at var(--sec-backdrop-x, 16%) var(--sec-backdrop-y, 74%),
      var(--site-accent-glow),
      transparent 62%
    );
    animation: apex-backdrop-drift 22s ease-in-out infinite;
  }
  /* ---- The same glow, on a LIGHT band ----
     A bloom works by being lighter than what is behind it. On a dark band that
     is exactly what the accent does. On a light band it is DARKER than the
     background, so the identical rule stops reading as light and starts reading
     as a stain: Joey, 2026-08-25, "the gradient or whatever is on looks weird."
     He spotted it on the home-and-office page and it was on every light band on
     every page, the signed-off auto tint page included.

     Two things made it read as a smudge rather than a glow, and both are fixed
     here rather than by switching the backdrop off. The band keeps its depth.
       - Intensity. The accent at 14% over near-white is a visible pink wash.
       - The edge. Ending at 62% puts the falloff well inside the band, so the
         boundary lands on flat colour with nothing to hide it. Widening the
         bloom and running it out to full transparency removes the edge entirely. */
  [data-site-template="apex"] [data-sec-backdrop="glow"][data-sec-band="light"] .site-sec::before {
    background: radial-gradient(
      68% 72% at var(--sec-backdrop-x, 16%) var(--sec-backdrop-y, 74%),
      var(--site-accent-glow),
      transparent 100%
    );
    opacity: 0.3;
  }
  @keyframes apex-backdrop-drift {
    50% { transform: translate3d(2.5%, -2%, 0) scale(1.07); }
  }

  /* ---- Backdrop: giant ghosted word or logo mark ----
     Either a single huge word set in the display face, or the shop's own logo
     masked so it can be tinted to the band. Both sit at a whisper of opacity.
     The word comes through a custom property so no markup is needed.

     The mark HANGS OFF A CORNER and bleeds past the band edge (the section clips
     it), rather than sitting centred. Centred, a word far wider than the band
     showed only its middle letters and read as an even wash, so the whole
     treatment was invisible and the band looked empty. Which corner comes from
     data-sec-mark-x / -y, derived from the same x / y the glow uses. */
  [data-site-template="apex"] [data-sec-backdrop="mark"] .site-sec::before {
    /* Split out of the old single backdrops switch, so a look can lose the giant
       words and keep the glows. See the note on the glow rule above. */
    display: var(--orn-ghost-word, block);
    content: var(--sec-backdrop-text, "");
    position: absolute;
    z-index: 0;
    pointer-events: none;
    font-family: var(--site-font-head);
    font-weight: 900;
    text-transform: uppercase;
    white-space: nowrap;
    line-height: 0.74;
    letter-spacing: -0.02em;
    font-variation-settings: 'wdth' 112;
    font-size: var(--sec-backdrop-size, min(17vw, 240px));
    color: var(--site-fg);
    /* The design's own values. A solid word at this size only needs a whisper;
       any more and it competes with the heading sitting over it. */
    opacity: 0.036;
  }
  /* THE BLEED IS A SHARE OF THE WORD, NOT OF THE BAND.
     These were left: -3% and right: -8%, and a percentage on left or right is
     measured against the BAND, not against the word sitting in it. So the same
     offset took a sliver off a long word and a whole letter off a short one.
     Measured at 1920: "Frederick" lost 4 percent of itself and "Start" lost 18,
     which is why that band read as "STAR". A translate percentage is measured
     against the ELEMENT, so every word now loses the same small share of itself
     however long or short it is, and the design's deliberate bleed is kept. */
  [data-site-template="apex"] [data-sec-backdrop="mark"][data-sec-mark-x="left"] .site-sec::before {
    left: 0; transform: translateX(-4%);
  }
  [data-site-template="apex"] [data-sec-backdrop="mark"][data-sec-mark-x="right"] .site-sec::before {
    right: 0; transform: translateX(4%);
  }
  [data-site-template="apex"] [data-sec-backdrop="mark"][data-sec-mark-y="top"] .site-sec::before { top: 2%; }
  [data-site-template="apex"] [data-sec-backdrop="mark"][data-sec-mark-y="bottom"] .site-sec::before { bottom: -4%; }
  /* Logo-mark flavour: no text, the shop's logo masked over a flat fill. It needs
     a real box (a mask has no intrinsic size), sized as a share of the band so it
     scales with the viewport the same way the word does. */
  [data-site-template="apex"] [data-sec-backdrop="mark"][data-sec-backdrop-mark="logo"] .site-sec::before {
    content: "";
    width: 46%;
    height: 38%;
    background: var(--site-fg);
    opacity: 0.055;
    -webkit-mask-image: var(--sec-backdrop-image);
    mask-image: var(--sec-backdrop-image);
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
  }
  /* A word reads slightly hotter on white than on black, and a logo MASK reads
     weaker than a solid word either way, so each gets its own value. */
  [data-site-template="apex"] [data-sec-band="light"][data-sec-backdrop="mark"] .site-sec::before { opacity: 0.034; }
  [data-site-template="apex"] [data-sec-band="light"][data-sec-backdrop="mark"][data-sec-backdrop-mark="logo"] .site-sec::before { opacity: 0.05; }

  /* ---- The companion bloom on a mark ----
     The second layer, and the only stacking the system allows. The design pairs a
     ghosted word or logo hanging off one corner with a soft accent bloom in the
     opposite one, which is what stops a big flat band reading as dead space in the
     corner the mark does not reach.
     ::before is the mark, so the bloom takes ::after. Photo already uses ::after
     for its edge veil, but photo can never carry a companion, so the two never
     meet. Same drift and the same accent-driven colour as a primary glow, so a
     shop whose accent is blue gets a blue bloom. */
  [data-site-template="apex"] [data-sec-backdrop="mark"][data-sec-glow] .site-sec::after {
    /* A bloom is a glow, so it follows the glow switch and not the word one. */
    display: var(--orn-band-glow, block);
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background: radial-gradient(
      44% 48% at var(--sec-glow-x, 84%) var(--sec-glow-y, 24%),
      var(--site-accent-glow),
      transparent 62%
    );
    animation: apex-backdrop-drift 26s ease-in-out infinite;
  }

  /* =========================================================================
     NAV + FOOTER
     ========================================================================= */

  /* Accent slash separators between the nav links, and a tighter uppercase
     display treatment on them. */
  [data-site-template="apex"] .site-nav-links a {
    font-family: var(--site-font-head);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.04em;
  }
  [data-site-template="apex"] .site-nav-links li + li { position: relative; }
  [data-site-template="apex"] .site-nav-links li + li::before {
    content: "/";
    color: var(--site-accent);
    font-weight: 800;
    font-style: italic;
    opacity: 0.9;
    margin-right: 13px;
  }
  [data-site-template="apex"] .site-nav-cta { padding: 6px 6px 6px 20px; font-size: 12px; }
  [data-site-template="apex"] .site-nav-cta .site-nav-cta-arrow { width: 31px; height: 31px; }

  /* Footer: the accent top rule is already there; Apex just tightens the
     headings into the display face. */
  [data-site-template="apex"] footer h4 {
    font-family: var(--site-font-head);
    text-transform: uppercase;
    letter-spacing: 0.2em;
    font-weight: 900;
  }

  /* =========================================================================
     CRAFT DETAILS
     Small shared pieces the Apex sections opt into by class. They live here
     rather than in a component so any Apex section can use them and they stay
     accent-driven.
     ========================================================================= */

  /* Vertical side label running up the left edge of a band. */
  [data-site-template="apex"] .site-vlabel {
    display: var(--orn-side-label, flex);
    position: absolute;
    left: 14px;
    top: 50%;
    transform: translateY(-50%);
    writing-mode: vertical-rl;
    rotate: 180deg;
    font-size: 10.5px;
    letter-spacing: 0.42em;
    text-transform: uppercase;
    color: var(--site-subtle);
    align-items: center;
    gap: 14px;
    z-index: 3;
    pointer-events: none;
  }
  [data-site-template="apex"] .site-vlabel::after {
    content: "";
    width: 1px;
    height: 66px;
    background: linear-gradient(to bottom, var(--site-accent), transparent);
  }
  @media (max-width: 1100px) { [data-site-template="apex"] .site-vlabel { display: none; } }

  /* Stat row with an accent top rule. */
  [data-site-template="apex"] .site-statrow { border-top: 2px solid var(--site-accent); }

  /* Rotated marquee strip in the accent colour. */
  [data-site-template="apex"] .site-marquee-strip {
    background: var(--site-accent);
    color: var(--site-btn-fg, #fff);
    /* One variable holds the lean, so the over-scale that runs the ends off the
       page can be worked out from it: a flat strip needs no over-scale and would
       otherwise sit 5 per cent too wide. */
    transform: rotate(var(--orn-strip-tilt, -1.1deg)) scale(var(--orn-strip-scale, 1.05));
  }

  /* =========================================================================
     MOTION
     Everything decorative stops for anyone who asks for reduced motion.
     ========================================================================= */
  @media (prefers-reduced-motion: reduce) {
    [data-site-template="apex"] [data-sec-backdrop="glow"] .site-sec::before,
    [data-site-template="apex"] [data-sec-backdrop="mark"][data-sec-glow] .site-sec::after { animation: none; }
    [data-site-template="apex"] .site-btn { transition: none; }
    [data-site-template="apex"] .site-btn:hover { transform: none; }
    [data-site-template="apex"] .site-btn-arrow,
    [data-site-template="apex"] .site-nav-cta-arrow { transition: none; }
  }

  
  /* =========================================================================
     SHARED APEX HOME CRAFT
     ========================================================================= */

  /* Scroll reveal. The section components add .in once the block enters the
     viewport. The translate property is used rather than transform on purpose: the
     skewed work tiles and the staggered service cards already own their
     transform, and animating it here would silently cancel theirs. */
  [data-site-template="apex"] .ax-rv {
    /* Switched off by starting the block already arrived, never by removing the
       transition alone: .ax-rv begins invisible and is brought in by a class the
       component adds on scroll, so killing only the movement would leave a page
       of blank bands. */
    opacity: var(--orn-reveal-opacity, 0);
    translate: var(--orn-reveal-shift, 0 24px);
    transition: var(--orn-reveal-ease, opacity .85s cubic-bezier(.2,.7,.3,1), translate .85s cubic-bezier(.2,.7,.3,1));
  }
  [data-site-template="apex"] .ax-rv.in { opacity: 1; translate: none; }

  /* Section header: heading block on the left, a short lead or a button on the
     right, both dropping to one column when there is no room. */
  [data-site-template="apex"] .ax-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 40px;
    flex-wrap: wrap;
    margin-bottom: 44px;
  }
  [data-site-template="apex"] .ax-head-main { min-width: min(100%, 420px); flex: 1 1 460px; }
  [data-site-template="apex"] .ax-head h2 { max-width: 21ch; margin: 0; font-size: clamp(28px, 3.2vw, 46px); }
  [data-site-template="apex"] .ax-head-side { flex: 0 1 34ch; }
  [data-site-template="apex"] .ax-lead {
    margin: 0;
    font-size: clamp(15px, 1.02vw, 16.5px);
    line-height: 1.72;
    color: var(--site-muted);
    max-width: 58ch;
  }
  [data-site-template="apex"] .ax-sec-eyebrow { margin-bottom: 20px; }

  /* WHERE THE CONTENT SITS, for the two column bands.
     The shared rules in SiteThemeProvider move the heading block itself (it
     carries .site-sec-head). These say what the SECOND column does, which is the
     one thing a shared rule cannot know. At centre the two columns stack and
     centre. At left or right they stay two columns and swap order, so the
     heading takes the side the shop picked and the lead sits opposite it.
     Nothing here matches unless the band carries a chosen alignment. */
  [data-site-template="apex"] [data-sec-align="left"] .ax-head { flex-direction: row; }
  [data-site-template="apex"] [data-sec-align="right"] .ax-head { flex-direction: row-reverse; }
  [data-site-template="apex"] [data-sec-align="right"] .ax-head-side { text-align: right; }
  [data-site-template="apex"] [data-sec-align="right"] .ax-lead { margin-left: auto; }
  [data-site-template="apex"] [data-sec-align="center"] .ax-head { flex-direction: column; align-items: center; }
  [data-site-template="apex"] [data-sec-align="center"] .ax-head-main { flex: 0 1 auto; }
  [data-site-template="apex"] [data-sec-align="center"] .ax-head-side { flex: 0 1 auto; text-align: center; }
  [data-site-template="apex"] [data-sec-align="center"] .ax-lead { margin-left: auto; margin-right: auto; }

  /* The closing band is the same two column shape (words, then buttons), so it
     gets the same answer. */
  [data-site-template="apex"] [data-sec-align="right"] .ax-cta-in { flex-direction: row-reverse; }
  [data-site-template="apex"] [data-sec-align="center"] .ax-cta-in { flex-direction: column; align-items: center; }

  /* Tabular figures everywhere a number animates, so a count-up never jitters. */
  [data-site-template="apex"] .ax-num { font-variant-numeric: tabular-nums; }

  /* An eyebrow the shop left blank must not paint a stray marker bar (the bar is
     the eyebrow's own background, so an empty one shows as a bare accent smudge).
     The Apex sections handle this properly in ApexEyebrow, which does not render
     the element at all when there is no real text. This rule is the safety net
     for the CLASSIC sections rendered under Apex on every non-home page, which
     render their eyebrow directly. It only catches a truly empty element, not one
     holding whitespace, which is exactly why the component check exists too. */
  [data-site-template="apex"] .site-eyebrow:empty { display: none; }

  /* The backdrop layer lifts a section's direct children to z-index 1 so they
     sit above the backdrop. That rule also forces position:relative, which
     would drop these two decorations into the flow and push the whole band
     down. Re-assert absolute at matching specificity, after it in the cascade. */
  [data-site-template="apex"] [data-sec-backdrop]:not([data-sec-backdrop="none"]) .site-sec > .site-vlabel,
  [data-site-template="apex"] [data-sec-backdrop]:not([data-sec-backdrop="none"]) .site-sec > .ax-cta-bloom {
    position: absolute;
  }

  /* The display face at the two sizes Apex uses outside h1 / h2. */
  [data-site-template="apex"] .ax-d {
    font-family: var(--site-font-head);
    font-weight: 900;
    text-transform: uppercase;
    letter-spacing: -0.004em;
  }

  /* =========================================================================
     HERO
     ========================================================================= */
  /* The hero is sized so the HIGHLIGHT STRIP below it lands at the bottom of the
     first screen, which is what tells a visitor there is more page under it.
     Everything above the hero (the announcement strip and the sticky nav) plus
     the strip and its gap comes to roughly 250px, so the hero takes the screen
     minus that. It is a min-height, so a shop with a long headline or a stat row
     simply grows the band rather than clipping any of it, and the clamp keeps the
     band sane on a very short window and on a very tall monitor.

     svh (the small viewport height) rather than vh, so a phone or tablet browser
     that hides its own chrome on scroll cannot make the hero taller than the
     screen it was measured against. The vh line above it is the fallback for a
     browser that does not know svh. */
  [data-site-template="apex"] .ax-hero {
    position: relative;
    min-height: clamp(440px, calc(100vh - 250px), 720px);
    min-height: clamp(440px, calc(100svh - 250px), 720px);
    display: flex;
    align-items: flex-end;
    padding: 64px 0 0;
    overflow: hidden;
    background: #000;
  }
  /* The photo sits slightly oversized so the slow parallax never exposes an
     edge. The transform is written by the component on scroll. */
  [data-site-template="apex"] .ax-hero-media { position: absolute; inset: -6% 0 0; z-index: 0; }
  [data-site-template="apex"] .ax-hero-media img { width: 100%; height: 100%; object-fit: cover; }
  /* The controllable dim. Two layers built from ONE resolved image-overlay
     setting (the existing gear / Site Style control), so a shop tunes it with
     the control it already has:
       --ax-hero-veil  a directional wash across the text column, which is what
                       keeps a huge white headline readable over a BRIGHT photo
       --ax-hero-scrim the familiar bottom-up gradient, which keeps the lower
                       copy and the stat row readable over a DARK photo
     Both are the same colour, so changing the overlay moves them together. */
  [data-site-template="apex"] .ax-hero-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background: var(--ax-hero-veil), var(--ax-hero-scrim);
  }
  [data-site-template="apex"] .ax-hero-glow {
    /* --orn-hero-glow, unset, is the drifting glow the design ships with. */
    display: var(--orn-hero-glow, block);
    position: absolute;
    inset: -25% -12%;
    z-index: 1;
    pointer-events: none;
    background: radial-gradient(42% 46% at var(--ax-gx,12%) var(--ax-gy,58%), var(--site-accent-glow), transparent 62%);
    animation: ax-drift 22s ease-in-out infinite;
    /* THE GLOW USED TO BE CENTRED ON THE HERO'S BOTTOM EDGE, and that is why it
       could only ever be cut. This box is inset -25% top and bottom, so it
       stands 1.5 hero-heights tall and the hero's bottom edge falls at
       (0.25 + 1) / 1.5 = 83.3% down it. The old 80% centre sat at 95% of the
       hero's height, right on that edge, so the hero's own overflow:hidden
       sliced the glow at its brightest and left a hard horizontal line across
       the page, with flat black below it.

       No fade could rescue that: a glow centred on the edge cannot reach nothing
       before the edge. So the centre moved up to the hero's lower third, where a
       low grounded glow was always the intent, and the mask guarantees it has
       reached nothing by the cut. Anyone changing the -25% inset has to change
       both numbers with it. */
    -webkit-mask-image: linear-gradient(to bottom, #000 64%, transparent 83%);
    mask-image: linear-gradient(to bottom, #000 64%, transparent 83%);
  }
  @keyframes ax-drift { 50% { transform: translate3d(2.5%, -2%, 0) scale(1.07); } }
  [data-site-template="apex"] .ax-hero-inner { position: relative; z-index: 3; padding-bottom: 28px; }
  [data-site-template="apex"] .ax-hero-copy { max-width: 860px; }
  [data-site-template="apex"] .ax-hero h1 {
    margin: 0 0 18px;
    font-size: clamp(38px, 5.2vw, 74px);
    color: var(--ax-hero-fg, #fff);
  }
  [data-site-template="apex"] .ax-hero-sub { color: var(--ax-hero-sub, rgba(255,255,255,.76)); max-width: 56ch; }
  [data-site-template="apex"] .ax-hero-btns { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }

  /* Vertical side label running up the left edge of the hero. */
  [data-site-template="apex"] .ax-side {
    display: var(--orn-hero-side, flex);
    position: absolute;
    left: 18px;
    top: 46%;
    writing-mode: vertical-rl;
    rotate: 180deg;
    font-size: 10.5px;
    letter-spacing: 0.42em;
    text-transform: uppercase;
    color: rgba(255,255,255,.45);
    align-items: center;
    gap: 14px;
    z-index: 3;
    pointer-events: none;
  }
  [data-site-template="apex"] .ax-side::after {
    content: "";
    width: 1px;
    height: 60px;
    background: linear-gradient(to bottom, var(--site-accent), transparent);
  }

  /* Stat row: four cells with an accent top rule, tucked into the hero. */
  [data-site-template="apex"] .ax-stats {
    position: relative;
    z-index: 4;
    display: grid;
    grid-template-columns: repeat(var(--ax-stat-cols, 4), minmax(0, 1fr));
    /* The panel, its top rule and its hairlines are four separate switches, so
       a look can frost the box, drop the accent line, or take the box away and
       leave the numbers on the photo. Every fallback below is the shipped
       design. */
    background: var(--orn-stats-bg, rgba(18,21,26,.86));
    -webkit-backdrop-filter: var(--orn-stats-blur, none);
    backdrop-filter: var(--orn-stats-blur, none);
    border: none;
    border-top: var(--orn-stats-rule, 2px) solid var(--site-accent);
    margin-top: 26px;
  }
  [data-site-template="apex"] .ax-stat { padding: 22px 26px; border-right: 1px solid var(--orn-stats-divider, rgba(255,255,255,.12)); min-width: 0; }
  [data-site-template="apex"] .ax-stat:last-child { border-right: 0; }
  [data-site-template="apex"] .ax-stat b {
    display: block;
    font-family: var(--site-font-head);
    font-weight: 900;
    font-size: clamp(25px, 2.3vw, 36px);
    line-height: 1;
    color: #fff;
  }
  [data-site-template="apex"] .ax-stat span {
    display: block;
    margin-top: 7px;
    font-size: 10.5px;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: rgba(255,255,255,.46);
  }

  /* =========================================================================
     HERO: THE OTHER ARRANGEMENTS
     =========================================================================
     The six arrangements are declared ONCE, in HERO_LAYOUTS, and read off the
     one 'home:hero' settings key both templates' heroes already share. What is
     written here is Apex's drawing of the five that are not the shipped one.

     EVERY rule is under [data-hero-layout="..."], and the shipped arrangement
     stamps NO attribute (see heroLayoutAttr), so not one of them can match an
     Apex hero nobody has touched. That is what makes "the Apex hero is
     unchanged" a property of the selectors rather than a promise.

     None of them carries !important, so the alignment control still wins where
     an arrangement has expressed a preference. */

  /* ---- SPLIT: photo one side, words the other, nothing over the photo ---- */
  [data-site-template="apex"] .ax-hero[data-hero-layout="split"] {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: stretch;
    padding: 0;
    background: var(--ax-hero-slab, #000);
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="split"] .ax-hero-media {
    position: relative; inset: auto; grid-column: 2; grid-row: 1; min-height: 100%;
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="split"] .ax-side { display: none; }
  /* The words keep the page's own content column, so the headline still lines up
     with every heading further down the page instead of hugging its half. The
     column is exactly half the band, so the inset the page uses works out as
     100% minus half the content width plus the gutter, which is the same
     expression the photo and video block already uses for the same reason. The
     max() is what keeps it sane once the band is narrower than the content
     column. No vw anywhere: a reserved scrollbar is counted in 100vw and is the
     classic way a full width band gains a horizontal scrollbar. */
  [data-site-template="apex"] .ax-hero[data-hero-layout="split"] .ax-hero-inner {
    grid-column: 1; grid-row: 1;
    display: flex; flex-direction: column; justify-content: center;
    padding-top: clamp(56px, 6vw, 92px);
    padding-bottom: clamp(56px, 6vw, 92px);
    padding-left: max(
      var(--site-gutter),
      calc(100% - var(--site-content-max, 1760px) / 2 + var(--site-gutter))
    );
    padding-right: clamp(26px, 3.4vw, 60px);
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="split"] .ax-hero-copy { max-width: 46rem; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="split"] h1 { font-size: clamp(34px, 3.8vw, 62px); }
  [data-site-template="apex"] .ax-hero[data-hero-layout="split"] .ax-stats { margin-top: 30px; }

  /* ---- CENTERED: a narrow column in the middle of the photo -------------- */
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"] { align-items: center; padding: 64px 0; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"] .ax-hero-scrim {
    background:
      linear-gradient(var(--ax-hero-wash), var(--ax-hero-wash)),
      var(--ax-hero-veil),
      var(--ax-hero-scrim);
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"] .ax-hero-inner {
    display: flex; flex-direction: column; align-items: center; text-align: center;
    padding-bottom: 0;
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"] .ax-hero-copy { max-width: 60rem; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"] h1 { margin-left: auto; margin-right: auto; max-width: 17ch; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"] .ax-hero-sub { margin-left: auto; margin-right: auto; }
  /* THE CENTRING STEPS ASIDE WHEN THE SHOP PICKS A SIDE. The two auto side
     margins above are what puts this arrangement's words in the middle, and an
     auto margin beats the align-items the shared alignment rules set, so
     without these two lines a shop that chose Left or Right got its heading,
     buttons and numbers on that side and its supporting line still floating in
     the middle of the column. Scoped to a chosen alignment, so an untouched
     centred hero renders exactly as before. */
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"][data-sec-align="left"] h1,
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"][data-sec-align="left"] .ax-hero-sub { margin-left: 0; margin-right: auto; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"][data-sec-align="right"] h1,
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"][data-sec-align="right"] .ax-hero-sub { margin-left: auto; margin-right: 0; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"] .ax-hero-btns { justify-content: center; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="centered"] .ax-stats { width: 100%; }

  /* ---- PANEL: the words in a solid box, the photo left bright ------------ */
  [data-site-template="apex"] .ax-hero[data-hero-layout="panel"] .ax-hero-scrim { display: none; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="panel"] .ax-hero-copy {
    max-width: 46rem;
    padding: clamp(28px, 3vw, 46px) clamp(24px, 2.8vw, 44px);
    background: var(--ax-hero-panel);
    border-left: 4px solid var(--site-accent);
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="panel"] h1 { font-size: clamp(32px, 4vw, 62px); max-width: 15ch; }
  /* Flush under the panel and the same width, so the words and the numbers read
     as one slab standing on the photo rather than two boxes that nearly line up. */
  [data-site-template="apex"] .ax-hero[data-hero-layout="panel"] .ax-stats { max-width: 46rem; margin-top: 0; }

  /* ---- COMPACT: a short masthead, not a poster --------------------------- */
  [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] {
    min-height: clamp(280px, 40vh, 420px);
    padding-top: 48px;
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .ax-side { display: none; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .ax-hero-scrim {
    background:
      linear-gradient(var(--ax-hero-wash), var(--ax-hero-wash)),
      var(--ax-hero-scrim);
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .ax-hero-copy {
    max-width: none;
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    column-gap: clamp(24px, 3vw, 52px); align-items: center;
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .site-eyebrow { grid-column: 1; }
  [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] h1 {
    grid-column: 1; max-width: 22ch; margin: 0;
    font-size: clamp(28px, 3.2vw, 50px);
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .ax-hero-sub {
    grid-column: 1; max-width: 52ch; margin-top: 12px;
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .ax-hero-btns {
    grid-column: 2; grid-row: 1 / span 3; align-self: center; margin-top: 0;
  }
  [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .ax-stats { margin-top: 20px; }

  /* ---- A SHOP WITH NO PHOTO ---------------------------------------------
     Only ever reached by an arrangement the shop CHOSE. The band dresses itself
     in the shop's own field rather than leaving a black hole, and Split has
     nothing to put in its second column so it stops asking for one. */
  [data-site-template="apex"] .ax-hero[data-hero-layout][data-hero-photo="none"] { background: var(--ax-hero-slab, #000); }
  [data-site-template="apex"] .ax-hero[data-hero-layout="split"][data-hero-photo="none"] {
    grid-template-columns: minmax(0, 1fr);
  }
  /* One column again, so the wrap centres itself the ordinary way and the
     half-band inset above would throw the words into the middle of the page. */
  [data-site-template="apex"] .ax-hero[data-hero-layout="split"][data-hero-photo="none"] .ax-hero-inner {
    padding-left: var(--site-gutter); padding-right: var(--site-gutter);
  }

  /* =========================================================================
     ROTATED MARQUEE STRIP
     ========================================================================= */
  [data-site-template="apex"] .ax-marquee {
    overflow: hidden;
    /* THE STRIP MUST NOT MAKE THE PAGE WIDER THAN THE WINDOW.
       Its track is deliberately wider than the screen, that is how a marquee
       works, and on the public site the strip is a plain block that clips it.
       Inside the dashboard editor the site is laid out shrink-to-fit so it can
       be scaled, and there a shrink-to-fit ancestor asks every child how wide
       it WANTS to be. The track answered with its full length, so the whole
       site was laid out four thousand pixels wide and the shop was looking at a
       page zoomed most of the way off its own screen.
       Containment is the answer to exactly that question: the strip's width is
       settled by the space it is given, and its contents are not consulted. */
    contain: inline-size;
    padding: 13px 0;
    position: relative;
    z-index: 5;
    margin: 28px 0 -10px;
  }
  [data-site-template="apex"] .ax-mq-track {
    display: flex;
    gap: 28px;
    width: max-content;
    align-items: center;
    animation: ax-mq 32s linear infinite;
    /* Stopped rather than removed, so the words stay exactly where they are and
       only the movement goes. */
    animation-play-state: var(--orn-marquee-play, running);
  }
  /* One repeat of the phrase set. The component renders as many of these as it
     takes to fill the strip, so a shop with four short phrases never watches a
     screenful of empty accent colour go past. */
  [data-site-template="apex"] .ax-mq-set { display: flex; gap: 28px; align-items: center; flex: none; }
  [data-site-template="apex"] .ax-mq-track span {
    font-family: var(--site-font-head);
    font-weight: 900;
    text-transform: uppercase;
    font-size: 17px;
    white-space: nowrap;
  }
  [data-site-template="apex"] .ax-mq-track i {
    width: 8px; height: 8px;
    background: currentColor;
    transform: rotate(45deg);
    flex: none;
    opacity: .8;
  }
  /* One whole set, measured, plus the gap that sits before the next set begins.
     The 50% fallback is the old behaviour and is what runs for the instant
     before the strip has been measured. */
  @keyframes ax-mq { to { transform: translateX(calc(-1 * var(--ax-mq-shift, 50%))); } }

  /* =========================================================================
     SERVICE CARDS
     ========================================================================= */
  /* THE COLUMN COUNT AND THE CARD SHAPE COME FROM VARIABLES, so the gear can
     set them without touching this rule and, more importantly, without using
     !important. The responsive rules further down set grid-template-columns
     OUTRIGHT, so a phone still collapses to one column whatever the shop chose.
     Unset, both fall back to exactly what the band has always drawn. */
  [data-site-template="apex"] .ax-svc {
    display: grid;
    grid-template-columns: repeat(var(--ax-svc-cols, 4), minmax(0, 1fr));
    gap: 20px;
  }
  [data-site-template="apex"] .ax-card {
    position: relative;
    display: block;
    border-radius: 18px;
    overflow: hidden;
    aspect-ratio: var(--ax-card-ar, 1 / 1.4);
    background: #111;
    transition: transform .5s cubic-bezier(.2,.7,.3,1), box-shadow .5s ease;
  }
  /* Every other card drops lower, which is what stops the row reading as a
     plain grid. It collapses at the first breakpoint. */
  [data-site-template="apex"] .ax-card:nth-child(even) { margin-top: 36px; }
  [data-site-template="apex"] .ax-card:hover { transform: translateY(-9px); box-shadow: var(--site-shadow); }
  [data-site-template="apex"] .ax-card-ph { position: absolute; inset: 0; }
  [data-site-template="apex"] .ax-card-ph img {
    width: 100%; height: 100%; object-fit: cover;
    transition: transform .8s cubic-bezier(.2,.7,.3,1);
  }
  [data-site-template="apex"] .ax-card:hover .ax-card-ph img { transform: scale(var(--orn-photo-zoom, 1.08)); }
  [data-site-template="apex"] .ax-card-veil { position: absolute; inset: 0; background: var(--ax-card-scrim); }
  [data-site-template="apex"] .ax-card-body {
    position: absolute;
    inset: auto 0 0 0;
    padding: 22px;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 12px;
  }
  [data-site-template="apex"] .ax-card h3 {
    color: var(--ax-card-fg, #fff);
    max-width: 13ch;
    margin: 0;
    font-family: var(--site-font-head);
    font-weight: 900;
    text-transform: uppercase;
    font-size: clamp(17px, 1.4vw, 21px);
    line-height: 1.06;
  }
  [data-site-template="apex"] .ax-card-kicker {
    display: block;
    font-size: 10px;
    /* The design's kickers are one short line. A shop's own can run to two, and
       at the body's 1.65 leading those two lines sat almost a full line apart and
       crowded the card title. Tightened so a two-line kicker reads as one label. */
    line-height: 1.34;
    letter-spacing: 0.26em;
    text-transform: uppercase;
    color: var(--ax-card-kicker-color, var(--site-accent));
    margin-bottom: 8px;
    font-weight: 600;
    /* THE KICKER SITS ON ITS OWN SMALL CHIP. Measured on real photos it read at
       1.6 to 2.8 to 1 on a desktop and under 4 on a phone, halo and all, because
       no colour of red can be read over every photo. The chip is painted in the
       overlay colour the shop chose, so on the dark end of the scrim it all but
       disappears, and the words take the nearest tint of the shop's accent that
       reads 4.5 to 1 on it (worked out in ApexServices). Width follows the words;
       the trailing letter-space is taken off the right padding so they sit
       centred in it. Outside the band neither variable is set and it draws as
       it always did. */
    width: fit-content;
    max-width: 100%;
    padding: var(--ax-card-kicker-pad, 0);
    border-radius: 999px;
    background: var(--ax-card-kicker-bg, transparent);
  }
  [data-site-template="apex"] [data-sec-align="center"] .ax-card-kicker { margin-left: auto; margin-right: auto; }
  /* ---- The card's button ----
     The card used to end in a circular accent arrow that faded in on hover, and
     that was the whole call to action: unlabelled, invisible until hovered, and
     on a phone never shown at all. The button is that arrow given words. It sits
     on its own line under the name, it is always visible, and it is drawn as
     glass with a hairline rather than a solid slab so it reads as part of the
     photo card instead of a control dropped onto one. The card's hover fills it
     with the shop's accent, which is where the old arrow's colour went.

     Every one of these rules hangs off .has-cta, which the body only carries
     when it really draws a button, so a shop that turns the buttons off gets the
     card back exactly as it was, arrow and all. */
  [data-site-template="apex"] .ax-card.has-cta .ax-card-veil { background: var(--ax-card-scrim-tall, var(--ax-card-scrim)); }
  [data-site-template="apex"] .ax-card-body.has-cta {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    gap: 16px;
  }
  [data-site-template="apex"] .ax-card-cta {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    max-width: 100%;
    padding: 7px 7px 7px 15px;
    border-radius: 999px;
    border: none;
    background: color-mix(in srgb, #06060a 34%, transparent);
    backdrop-filter: blur(7px);
    color: var(--ax-card-fg, #fff);
    transition: background .35s ease, border-color .35s ease, color .35s ease;
  }
  [data-site-template="apex"] .ax-card-cta-t {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* The arrow keeps its own disc, so the button reads as the old pip with a
     label attached rather than as a different component. */
  [data-site-template="apex"] .ax-card-cta-a {
    width: 27px; height: 27px;
    border-radius: 999px;
    flex: none;
    display: grid;
    place-items: center;
    background: color-mix(in srgb, var(--ax-card-fg, #fff) 20%, transparent);
    transition: background .35s ease;
  }
  [data-site-template="apex"] .ax-card-cta-a svg { width: 14px; height: 14px; }
  [data-site-template="apex"] .ax-card:hover .ax-card-cta {
    background: var(--site-accent);
    border-color: var(--site-accent);
    color: var(--site-btn-fg, #fff);
  }
  [data-site-template="apex"] .ax-card:hover .ax-card-cta-a { background: color-mix(in srgb, var(--site-btn-fg, #fff) 22%, transparent); }

  /* The circular arrow that only appears on hover. Drawn only by a card with no
     button, which is a shop that turned the buttons off. */
  [data-site-template="apex"] .ax-card-pip {
    width: 37px; height: 37px;
    border-radius: 999px;
    background: var(--site-accent);
    color: var(--site-btn-fg, #fff);
    display: grid;
    place-items: center;
    flex: none;
    translate: 0 8px;
    opacity: 0;
    transition: translate .4s cubic-bezier(.2,.7,.3,1), opacity .4s ease;
  }
  [data-site-template="apex"] .ax-card:hover .ax-card-pip { translate: none; opacity: 1; }
  [data-site-template="apex"] .ax-svc-foot { margin-top: 52px; display: flex; justify-content: center; }

  /* ---- Service cards: the arrangements a shop can choose ----
     The band stamps data-sec-layout ONLY for a chosen arrangement, so the
     staggered band above is what every shop keeps until its owner picks
     something. Both choices drop the stagger; they differ in the widths.

     STRAIGHT is even rows, taking its column count from how many cards the shop
     really has (--ax-svc-cols), so three cards fill the row.
     MOSAIC lays the cards on six tracks, two to a row, one wide and one narrow
     with the wide one swapping sides each row (--ax-span, worked out by
     mosaicSpans so a row can never come out ragged). The fixed aspect ratio has
     to go for that, since a card four tracks wide would otherwise be enormous. */
  [data-site-template="apex"] .ax-svc[data-sec-layout] .ax-card:nth-child(even) { margin-top: 0; }
  [data-site-template="apex"] .ax-svc[data-sec-layout="straight"] {
    grid-template-columns: repeat(var(--ax-svc-cols, 4), minmax(0, 1fr));
  }
  [data-site-template="apex"] .ax-svc[data-sec-layout="mosaic"] { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  [data-site-template="apex"] .ax-svc[data-sec-layout="mosaic"] .ax-card {
    grid-column: span var(--ax-span, 2);
    aspect-ratio: auto;
    height: clamp(300px, 27vw, 420px);
  }

  /* =========================================================================
     CENTRE, ON A ROW THAT DOES NOT FILL
     -------------------------------------------------------------------------
     A grid of 1fr tracks cannot centre a part-full last row, so seven cards in
     four columns always left the gap hanging off the right. These bands lay the
     same grid on twice as many tracks with every card spanning two, which turns
     a gap of one column into two half tracks and lets the short row be nudged
     over by one of them. The stagger, the gaps and the card sizes are untouched.

     Only written when the band is set to centre AND the last row is actually
     short. A tidy count, or any other alignment, carries no attribute and
     renders exactly as it always did. Mosaic is excluded: it works out its own
     spans and never leaves a ragged row.

     ONLY ABOVE 1100px. The track count is worked out for the desktop column
     count, and at 1100px these bands drop to two columns and at 680px to one.
     Left unscoped, the attribute selector outranked those phone rules and drew
     eight 26px tracks on a 390px phone: every card 73px wide. Measured
     2026-09-14. Same fix the classic services band got the same day. */
  @media (min-width: 1101px) {
    [data-site-template="apex"] .ax-svc[data-center-row],
    [data-site-template="apex"] .ax-tiles[data-center-row],
    [data-site-template="apex"] .ax-films[data-center-row] {
      grid-template-columns: repeat(var(--ax-center-tracks, 8), minmax(0, 1fr));
    }
    [data-site-template="apex"] .ax-svc[data-center-row] > *,
    [data-site-template="apex"] .ax-tiles[data-center-row] > *,
    [data-site-template="apex"] .ax-films[data-center-row] > * {
      grid-column: span 2;
    }
    /* BOTH ENDS, not just the start. "grid-column: span 2" above leaves the END
       auto, and naming a start line on its own then resolves that auto end to a
       single track, so the first card of the short row came out half width. */
    [data-site-template="apex"] .ax-svc[data-center-row] > [data-center-start],
    [data-site-template="apex"] .ax-tiles[data-center-row] > [data-center-start],
    [data-site-template="apex"] .ax-films[data-center-row] > [data-center-start] {
      grid-column: var(--ax-center-start, 1) / span 2;
    }
  }

  /* The words inside a card follow the same setting. The card keeps its copy at
     the bottom, where a photo card wants it; centring moves it left to right,
     not up and down. */
  /* THE PREFIX ORDER MATTERS, AND IT WAS BACK TO FRONT HERE. The template
     attribute sits on the site root and the alignment wrapper sits inside it,
     so "[data-sec-align] [data-site-template="apex"]" describes a template nested inside an alignment
     wrapper, which never exists. The rule matched nothing, which is why Joey
     read it back as "still left aligned, the text is just centred inside the
     left block". Every other rule in this file has the template first.

     Both shapes of the body are covered. Without a button it is a row, so the
     horizontal axis is justify-content. With one it is a column, so the
     horizontal axis is align-items. Fixing only one leaves a card with a button
     stubbornly left while its neighbour centres. */
  [data-site-template="apex"] [data-sec-align="center"] .ax-card-body {
    justify-content: center;
    text-align: center;
  }
  [data-site-template="apex"] [data-sec-align="center"] .ax-card-body.has-cta {
    align-items: center;
    justify-content: flex-end;
  }
  [data-site-template="apex"] [data-sec-align="center"] .ax-card-body > * { text-align: center; }
  [data-site-template="apex"] [data-sec-align="center"] .ax-card h3 { margin-left: auto; margin-right: auto; }

  /* =========================================================================
     SHADE PICKER
     ========================================================================= */
  [data-site-template="apex"] .ax-shade { display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(30px, 4vw, 64px); align-items: center; }
  [data-site-template="apex"] .ax-viewport {
    position: relative;
    border: none;
    border-radius: 18px;
    overflow: hidden;
    aspect-ratio: 16 / 10.5;
    background: #000;
  }
  [data-site-template="apex"] .ax-viewport img { width: 100%; height: 100%; object-fit: cover; }
  /* The film layer IS the shade preview: one flat near-black sheet whose
     opacity is driven by the chosen VLT, so a darker shade darkens the photo. */
  [data-site-template="apex"] .ax-viewport-film {
    position: absolute; inset: 0;
    background: #04060a;
    transition: opacity .55s cubic-bezier(.2,.7,.3,1);
  }
  [data-site-template="apex"] .ax-viewport-frame {
    position: absolute; inset: 14px;
    border: none;
    border-radius: 10px;
    pointer-events: none;
  }
  [data-site-template="apex"] .ax-viewport-hint {
    position: absolute; right: 22px; top: 20px;
    font-size: 10.5px; letter-spacing: 0.24em; text-transform: uppercase;
    color: rgba(255,255,255,.6);
  }
  [data-site-template="apex"] .ax-viewport-tag {
    position: absolute; left: 22px; bottom: 20px;
    font-family: var(--site-font-head); font-weight: 900; text-transform: uppercase;
    font-size: 15px; letter-spacing: 0.05em;
    background: var(--site-accent); color: var(--site-btn-fg, #fff);
    padding: 7px 14px;
  }
  [data-site-template="apex"] .ax-shade-btns { display: flex; flex-wrap: wrap; gap: 9px; margin: 20px 0 26px; padding: 0; }
  [data-site-template="apex"] .ax-sbtn {
    font-family: var(--site-font-head);
    font-weight: 900;
    font-size: 15px;
    padding: 12px 20px;
    border-radius: 999px;
    border: none;
    background: transparent;
    color: var(--site-fg);
    cursor: pointer;
    transition: background .25s ease, border-color .25s ease, color .25s ease;
    font-variant-numeric: tabular-nums;
  }
  [data-site-template="apex"] .ax-sbtn:hover { border-color: var(--site-accent); }
  [data-site-template="apex"] .ax-sbtn[aria-pressed="true"] {
    background: var(--site-accent);
    border-color: var(--site-accent);
    color: var(--site-btn-fg, #fff);
  }
  [data-site-template="apex"] .ax-specs {
    display: grid;
    grid-template-columns: repeat(var(--ax-spec-cols, 3), minmax(0, 1fr));
    border: none;
    border-top: 2px solid var(--site-accent);
  }
  [data-site-template="apex"] .ax-spec { padding: 20px 22px; border-right: 1px solid var(--site-border); min-width: 0; }
  [data-site-template="apex"] .ax-spec:last-child { border-right: 0; }
  [data-site-template="apex"] .ax-spec b {
    display: block;
    font-family: var(--site-font-head); font-weight: 900;
    font-size: 30px; line-height: 1;
    font-variant-numeric: tabular-nums;
    color: var(--site-fg);
  }
  [data-site-template="apex"] .ax-spec span {
    display: block; margin-top: 7px;
    font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase;
    color: var(--site-subtle);
  }
  [data-site-template="apex"] .ax-legal {
    margin-top: 22px;
    display: flex; gap: 12px; align-items: flex-start;
    padding: 16px 20px;
    border-left: 3px solid var(--site-accent);
    background: var(--site-accent-glow);
  }
  [data-site-template="apex"] .ax-legal p { font-size: 13.5px; color: var(--site-muted); line-height: 1.6; margin: 0; }
  [data-site-template="apex"] .ax-legal b { color: var(--site-fg); }

  /* =========================================================================
     FILM LINEUP
     ========================================================================= */
  [data-site-template="apex"] .ax-films { display: grid; grid-template-columns: repeat(var(--ax-film-cols, 4), minmax(0, 1fr)); gap: 18px; }
  [data-site-template="apex"] .ax-film {
    position: relative;
    background: var(--site-card-bg);
    border: none;
    border-radius: 16px;
    padding: 26px 24px 24px;
    transition: transform .45s cubic-bezier(.2,.7,.3,1), box-shadow .45s ease, border-color .45s ease;
  }
  [data-site-template="apex"] .ax-film:hover { transform: translateY(-8px); box-shadow: var(--site-shadow); border-color: var(--site-accent); }
  [data-site-template="apex"] .ax-film-bar { position: absolute; left: 24px; right: 24px; top: 0; height: 3px; background: var(--site-accent); opacity: .25; }
  [data-site-template="apex"] .ax-film.is-best .ax-film-bar { opacity: 1; }
  [data-site-template="apex"] .ax-film-tier { font-size: 10px; letter-spacing: 0.26em; text-transform: uppercase; color: var(--site-accent); font-weight: 600; }
  [data-site-template="apex"] .ax-film h3 { margin: 12px 0 4px; font-size: clamp(17px, 1.4vw, 21px); color: var(--site-fg); }
  [data-site-template="apex"] .ax-film-brand { display: block; font-size: 12.5px; color: var(--site-muted); letter-spacing: 0.06em; }
  [data-site-template="apex"] .ax-meter { margin: 20px 0 6px; }
  [data-site-template="apex"] .ax-meter-row {
    display: flex; justify-content: space-between; gap: 10px;
    font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--site-muted); margin-bottom: 9px; font-weight: 600;
  }
  [data-site-template="apex"] .ax-meter-row b { color: var(--site-fg); font-family: var(--site-font-head); font-weight: 900; font-size: 15px; }
  [data-site-template="apex"] .ax-meter-track { height: 7px; background: var(--site-surface-2); border-radius: 99px; overflow: hidden; }
  [data-site-template="apex"] .ax-meter-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--site-accent), var(--site-accent-2));
    border-radius: 99px;
    width: 0;
    transition: width 1.1s cubic-bezier(.2,.7,.3,1);
  }
  [data-site-template="apex"] .ax-film ul { margin: 16px 0 0; padding: 0; }
  [data-site-template="apex"] .ax-film li {
    list-style: none;
    display: flex; gap: 9px; align-items: flex-start;
    font-size: 13.5px; color: var(--site-muted);
    padding: 5px 0; border-top: 1px dashed var(--site-border);
  }
  [data-site-template="apex"] .ax-film li i { width: 6px; height: 6px; background: var(--site-accent); transform: rotate(45deg); flex: none; margin-top: 7px; }
  [data-site-template="apex"] .ax-film-flag {
    position: absolute; right: 20px; top: -11px;
    background: var(--site-accent); color: var(--site-btn-fg, #fff);
    font-family: var(--site-font-head); font-weight: 900;
    font-size: 10.5px; letter-spacing: 0.16em; padding: 5px 12px; text-transform: uppercase;
  }

  /* =========================================================================
     PPF COVERAGE MATRIX
     ========================================================================= */
  [data-site-template="apex"] .ax-matrix-wrap { overflow-x: auto; border: none; padding: 0 22px; }
  [data-site-template="apex"] .ax-matrix { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 640px; }
  [data-site-template="apex"] .ax-matrix th, [data-site-template="apex"] .ax-matrix td { text-align: center; padding: 15px 10px; border-bottom: 1px solid var(--site-border); }
  [data-site-template="apex"] .ax-matrix thead th {
    font-family: var(--site-font-head); font-weight: 900;
    text-transform: uppercase; font-size: 12.5px; letter-spacing: 0.1em;
    color: var(--site-fg); border-bottom: 2px solid var(--site-accent);
  }
  [data-site-template="apex"] .ax-matrix tbody th { text-align: left; font-weight: 600; color: var(--site-fg); font-size: 14px; }
  [data-site-template="apex"] .ax-matrix .ax-yes { color: var(--site-accent); }
  [data-site-template="apex"] .ax-matrix .ax-no { color: var(--site-subtle); }
  [data-site-template="apex"] .ax-mat-foot { display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap; margin-top: 26px; align-items: center; }

  /* =========================================================================
     WHY US COLLAGE
     ========================================================================= */
  [data-site-template="apex"] .ax-split { display: grid; grid-template-columns: 1.05fr 1fr; gap: clamp(34px, 4.6vw, 70px); align-items: center; }
  /* The band's height is a VARIABLE rather than a number written straight into
     min-height, because the Feature arrangement needs the same height as a
     DEFINITE one (a grid cannot share out rows against a box that only has a
     floor). One value, read two ways, so the two can never drift apart. Every
     count and every width sets the variable exactly where it used to set the
     number, so what the default arrangement computes is unchanged. */
  [data-site-template="apex"] .ax-collage { --ax-collage-h: 520px; position: relative; min-height: var(--ax-collage-h); }
  [data-site-template="apex"] .ax-collage-img { position: absolute; border-radius: 16px; overflow: hidden; box-shadow: var(--site-shadow); }
  [data-site-template="apex"] .ax-collage-img img { width: 100%; height: 100%; object-fit: cover; max-width: none; }
  [data-site-template="apex"] .ax-c1 { left: 0; top: 0; width: 62%; height: 56%; }
  [data-site-template="apex"] .ax-c2 { right: 0; top: 9%; width: 35%; height: 38%; border: none; }
  [data-site-template="apex"] .ax-c3 { right: 4%; bottom: 0; width: 56%; height: 40%; }
  /* EVERY COUNT HAS ITS OWN COMPOSITION, because this is a collage and not a
     grid: there is no column count to work out, so each number is arranged for
     rather than derived. Three is what the band draws by default and is the base
     rule above, so a shop that never opens the control is untouched. One and two
     were already here. Four, five and six are the ones a shop can now ask for.
     The bottom left corner is kept clear in every one of them, because that is
     where the turning disc sits. */
  [data-site-template="apex"] .ax-collage[data-count="1"] { --ax-collage-h: 460px; }
  [data-site-template="apex"] .ax-collage[data-count="1"] .ax-c1 { left: 0; top: 0; width: 84%; height: 76%; }
  [data-site-template="apex"] .ax-collage[data-count="2"] { --ax-collage-h: 480px; }
  [data-site-template="apex"] .ax-collage[data-count="2"] .ax-c1 { left: 0; top: 0; width: 66%; height: 62%; }
  [data-site-template="apex"] .ax-collage[data-count="2"] .ax-c2 { right: 0; bottom: 6%; top: auto; width: 52%; height: 50%; }

  [data-site-template="apex"] .ax-collage[data-count="4"] { --ax-collage-h: 580px; }
  [data-site-template="apex"] .ax-collage[data-count="4"] .ax-c1 { left: 0; top: 0; width: 52%; height: 40%; }
  [data-site-template="apex"] .ax-collage[data-count="4"] .ax-c2 { right: 0; top: 6%; width: 42%; height: 34%; }
  [data-site-template="apex"] .ax-collage[data-count="4"] .ax-c3 { left: 4%; top: 44%; width: 44%; height: 30%; }
  [data-site-template="apex"] .ax-collage[data-count="4"] .ax-c4 { right: 4%; bottom: 0; width: 40%; height: 34%; }

  [data-site-template="apex"] .ax-collage[data-count="5"] { --ax-collage-h: 620px; }
  [data-site-template="apex"] .ax-collage[data-count="5"] .ax-c1 { left: 0; top: 0; width: 52%; height: 38%; }
  [data-site-template="apex"] .ax-collage[data-count="5"] .ax-c2 { right: 0; top: 0; width: 44%; height: 28%; }
  [data-site-template="apex"] .ax-collage[data-count="5"] .ax-c3 { right: 0; top: 32%; width: 32%; height: 26%; }
  [data-site-template="apex"] .ax-collage[data-count="5"] .ax-c4 { left: 6%; top: 42%; width: 44%; height: 30%; }
  [data-site-template="apex"] .ax-collage[data-count="5"] .ax-c5 { right: 6%; bottom: 0; width: 44%; height: 28%; }

  [data-site-template="apex"] .ax-collage[data-count="6"] { --ax-collage-h: 660px; }
  [data-site-template="apex"] .ax-collage[data-count="6"] .ax-c1 { left: 0; top: 0; width: 48%; height: 32%; }
  [data-site-template="apex"] .ax-collage[data-count="6"] .ax-c2 { right: 0; top: 0; width: 46%; height: 27%; }
  [data-site-template="apex"] .ax-collage[data-count="6"] .ax-c3 { right: 4%; top: 31%; width: 34%; height: 26%; }
  [data-site-template="apex"] .ax-collage[data-count="6"] .ax-c4 { left: 4%; top: 36%; width: 40%; height: 27%; }
  [data-site-template="apex"] .ax-collage[data-count="6"] .ax-c5 { right: 0; bottom: 0; width: 42%; height: 28%; }
  [data-site-template="apex"] .ax-collage[data-count="6"] .ax-c6 { left: 26%; bottom: 4%; width: 30%; height: 24%; }

  /* With fewer photos the badge sits clear of them rather than on top. */
  [data-site-template="apex"] .ax-collage[data-count="1"] .ax-stamp,
  [data-site-template="apex"] .ax-collage[data-count="2"] .ax-stamp { left: 4%; bottom: 0; }

  /* =========================================================================
     WHY US COLLAGE, THE FEATURE ARRANGEMENT
     -------------------------------------------------------------------------
     One big photo leading and the rest sized around it, with nothing left
     floating. Reached only by a shop that picks it in the section gear: none of
     these rules can match until data-sec-layout is stamped, so the arrangement
     the band ships with is untouched by every line below.

     ONE GRID, TWELVE BY TWELVE, AT EVERY WIDTH. Each count places its photos on
     the same grid, so there is one composition per number rather than one per
     number per screen size, and a phone gets the same design as a desktop with
     the spacing brought in. Twelve divides by two, three and four, which is what
     lets six photos share a row exactly.

     THE DISC HAS A SPOT OF ITS OWN, the bottom left three columns of the bottom
     four rows, kept clear by every composition here. The default arrangement
     leaves that corner clear too, but as a gap between scattered photos. Here it
     is a place in the grid, and the disc sits in it and shrinks to fit rather
     than lying across a photo where its ring cannot be read. A shop with no disc
     to draw closes the spot up (see data-disc below), so the corner is never an
     empty square.

     THE SPACING IS ONE NUMBER. Judged on the real band rather than scaled from a
     sketch: the photos here are 150 to 460 pixels across, and the same gap the
     scattered arrangement happens to leave between its photos reads as
     deliberate spacing at that size. A phone has a third of the width to give
     away, so it takes a third of the gap. */
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"] {
    --ax-collage-gap: 22px;
    display: grid;
    /* Definite, not a floor: fractional rows need a height to share out. It is
       the same value min-height already resolves to, so a Feature band is
       exactly as tall as the same band would have been. */
    height: var(--ax-collage-h);
    grid-template-columns: repeat(12, minmax(0, 1fr));
    grid-template-rows: repeat(12, minmax(0, 1fr));
    gap: var(--ax-collage-gap);
  }
  /* The photos stop placing themselves and start taking the cells they are
     given. Everything the scattered compositions set on them is undone here in
     one rule rather than count by count. */
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"] .ax-collage-img {
    position: relative; inset: auto; width: auto; height: auto;
  }
  /* The disc, sitting in its own spot rather than over the photos. It never
     grows past the size it is drawn at, and on a narrow screen it comes down to
     whatever its spot can hold, so it is always clear of every photo. */
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"] .ax-stamp {
    position: relative; inset: auto;
    grid-area: 9 / 1 / 13 / 4;
    width: min(100%, 112px); height: min(100%, 112px);
    place-self: center;
  }

  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="1"] .ax-c1 { grid-area: 1 / 1 / 9 / 13; }

  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="2"] .ax-c1 { grid-area: 1 / 1 / 9 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="2"] .ax-c2 { grid-area: 9 / 4 / 13 / 13; }

  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="3"] .ax-c1 { grid-area: 1 / 1 / 9 / 9; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="3"] .ax-c2 { grid-area: 1 / 9 / 9 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="3"] .ax-c3 { grid-area: 9 / 4 / 13 / 13; }

  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="4"] .ax-c1 { grid-area: 1 / 1 / 9 / 9; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="4"] .ax-c2 { grid-area: 1 / 9 / 5 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="4"] .ax-c3 { grid-area: 5 / 9 / 9 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="4"] .ax-c4 { grid-area: 9 / 4 / 13 / 13; }

  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="5"] .ax-c1 { grid-area: 1 / 1 / 9 / 9; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="5"] .ax-c2 { grid-area: 1 / 9 / 5 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="5"] .ax-c3 { grid-area: 5 / 9 / 9 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="5"] .ax-c4 { grid-area: 9 / 4 / 13 / 9; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="5"] .ax-c5 { grid-area: 9 / 9 / 13 / 13; }

  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="6"] .ax-c1 { grid-area: 1 / 1 / 9 / 9; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="6"] .ax-c2 { grid-area: 1 / 9 / 5 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="6"] .ax-c3 { grid-area: 5 / 9 / 9 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="6"] .ax-c4 { grid-area: 9 / 4 / 13 / 7; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="6"] .ax-c5 { grid-area: 9 / 7 / 13 / 10; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-count="6"] .ax-c6 { grid-area: 9 / 10 / 13 / 13; }

  /* NO DISC, NO SPOT HELD FOR ONE. A shop with no rating and no service names to
     ring gets no disc at all, and a spot held open for something that is never
     drawn is exactly the hole this arrangement exists to get rid of. So the
     photos along the bottom take the corner back, evenly. Only ever stamped when
     Feature is drawing, so no other shop can reach these. */
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-disc="none"][data-count="1"] .ax-c1 { grid-area: 1 / 1 / 13 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-disc="none"][data-count="2"] .ax-c2 { grid-area: 9 / 1 / 13 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-disc="none"][data-count="3"] .ax-c3 { grid-area: 9 / 1 / 13 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-disc="none"][data-count="4"] .ax-c4 { grid-area: 9 / 1 / 13 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-disc="none"][data-count="5"] .ax-c4 { grid-area: 9 / 1 / 13 / 7; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-disc="none"][data-count="5"] .ax-c5 { grid-area: 9 / 7 / 13 / 13; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-disc="none"][data-count="6"] .ax-c4 { grid-area: 9 / 1 / 13 / 5; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-disc="none"][data-count="6"] .ax-c5 { grid-area: 9 / 5 / 13 / 9; }
  [data-site-template="apex"] .ax-collage[data-sec-layout="feature"][data-disc="none"][data-count="6"] .ax-c6 { grid-area: 9 / 9 / 13 / 13; }

  /* A spot the shop asked for and has not filled. Edit mode only: the band drops
     these off the public site, so a visitor never sees an outline. */
  [data-site-template="apex"] .ax-collage-empty {
    border: 1px dashed var(--site-border); box-shadow: none; background: var(--site-panel);
  }
  /* The per-photo edit controls, stacked in the top right of their own slot. */
  [data-site-template="apex"] .ax-collage-edit {
    position: absolute; top: 8px; right: 8px; z-index: 5;
    display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
  }
  /* Slowly rotating circular badge. The RING turns, the mark in the middle of it
     does not, because that mark now carries a number and a number that spends
     half its life upside down is not a number anyone can read. */
  [data-site-template="apex"] .ax-stamp { display: var(--orn-stamp, block); position: absolute; left: 2%; bottom: 6%; width: 112px; height: 112px; }
  [data-site-template="apex"] .ax-stamp-spin {
    animation: ax-spin 30s linear infinite;
    transform-box: view-box; transform-origin: 56px 56px;
  }
  /* --ax-ring-size is set on the element ONLY when a shop's numbers are too long
     for the circle at its normal size, so a disc that fits carries no inline
     style at all and computes exactly what this line says. */
  [data-site-template="apex"] .ax-stamp text { font-family: var(--site-font-body); font-size: var(--ax-ring-size, 9.2px); letter-spacing: 0.3em; fill: var(--site-muted); text-transform: uppercase; }
  [data-site-template="apex"] .ax-stamp-star { fill: var(--site-accent); }
  /* Beats the rule above it on specificity, which it has to: the number is a
     <text> too, and it wants none of the ring's size, tracking or casing. The
     ink is the same one every accent-filled button uses, so a shop with a pale
     accent gets dark digits without configuring anything. */
  [data-site-template="apex"] .ax-stamp .ax-stamp-num {
    font-family: var(--site-font-head); font-weight: 900; font-size: 13px;
    letter-spacing: 0; text-transform: none; fill: var(--site-btn-fg, #fff);
  }
  @keyframes ax-spin { to { transform: rotate(360deg); } }
  [data-site-template="apex"] .ax-rows { display: grid; gap: 22px; margin: 26px 0 30px; }
  [data-site-template="apex"] .ax-row h3 { font-size: 16.5px; margin: 0 0 7px; color: var(--site-fg); }
  [data-site-template="apex"] .ax-row-bar { width: 26px; height: 3px; background: var(--site-accent); margin-bottom: 12px; }
  [data-site-template="apex"] .ax-google {
    display: flex; align-items: center; gap: 13px;
    padding: 13px 19px; border: none;
    width: max-content; max-width: 100%;
  }
  [data-site-template="apex"] .ax-stars { display: flex; gap: 3px; color: var(--site-accent); }
  [data-site-template="apex"] .ax-google b { font-family: var(--site-font-head); font-weight: 900; font-size: 18px; color: var(--site-fg); }
  [data-site-template="apex"] .ax-google small { color: var(--site-muted); font-size: 12.5px; }

  /* =========================================================================
     NUMBERED PROCESS
     ========================================================================= */
  [data-site-template="apex"] .ax-steps { display: grid; grid-template-columns: repeat(var(--ax-step-cols, 4), minmax(0, 1fr)); gap: 24px; margin-top: 12px; position: relative; }
  [data-site-template="apex"] .ax-steps::before { content: ""; position: absolute; left: 0; right: 0; top: 26px; height: 1px; background: var(--orn-process-line, var(--site-border)); }
  /* --ax-step-no-w is the ONE number that keeps the outlined numeral and the step
     title apart. The numeral is boxed to exactly that width and pinned to the
     column's right edge; the title reserves the same width plus a gap as
     padding-right. Because it is padding rather than a one-off nudge, EVERY line
     of the title stays clear of the numeral, so a title that wraps to two or
     three lines cannot run into it at any viewport width. */
  [data-site-template="apex"] .ax-step { position: relative; padding-top: 58px; --ax-step-no-w: 56px; }
  [data-site-template="apex"] .ax-step::before {
    content: ""; position: absolute; left: 0; top: 20px;
    width: 13px; height: 13px; border-radius: 99px;
    background: var(--site-bg); border: 2px solid var(--site-accent);
  }
  /* Ghosted outlined numeral. Condensed, which is both what the design uses and
     what lets two digits sit comfortably inside the reserve. */
  [data-site-template="apex"] .ax-step-no {
    position: absolute; right: 0; top: 46px;
    box-sizing: border-box;
    width: var(--ax-step-no-w);
    /* The outline stroke paints ~0.7px outside the glyph, so the box needs a
       hair of room or the last numeral loses its right edge to the clip. */
    padding-right: 3px;
    overflow: hidden;
    /* Never split the numeral. The box is sized for a condensed face, and a
       shop on a wider font (Poppins has no width axis) otherwise had "01"
       broken onto two lines by the site's overflow-wrap rule. */
    white-space: nowrap;
    text-align: right;
    font-family: var(--site-font-head); font-weight: 900;
    font-variation-settings: 'wdth' 78;
    font-size: 50px; line-height: 1;
    color: transparent;
    -webkit-text-stroke: 1.4px var(--site-accent);
    opacity: .42;
    pointer-events: none;
  }
  [data-site-template="apex"] .ax-step h3 {
    margin: 0 0 8px;
    padding-right: calc(var(--ax-step-no-w) + 16px);
    font-size: clamp(16px, 1.3vw, 20px);
    color: var(--site-fg);
  }
  [data-site-template="apex"] .ax-step p { color: var(--site-muted); font-size: 14px; line-height: 1.62; margin: 0; }

  /* =========================================================================
     RECENT WORK TILES
     ========================================================================= */
  [data-site-template="apex"] .ax-tiles { display: grid; grid-template-columns: repeat(var(--ax-tile-cols, 4), minmax(0, 1fr)); gap: 18px; }
  [data-site-template="apex"] .ax-tile {
    position: relative; overflow: hidden;
    transform: skewX(var(--orn-tile-skew, -8deg));
    background: #111; margin: 0;
    transition: transform .5s cubic-bezier(.2,.7,.3,1);
  }
  [data-site-template="apex"] .ax-tile:nth-child(odd) { height: 326px; margin-top: 44px; }
  [data-site-template="apex"] .ax-tile:nth-child(even) { height: 390px; }
  /* The photo layer is over-sized and counter-skewed so the parallelogram is
     filled edge to edge. max-width:none is required on the image itself, the
     global img rule would otherwise clamp it. */
  [data-site-template="apex"] .ax-tile-ph {
    position: absolute;
    left: var(--orn-tile-ph-x, -22%); top: var(--orn-tile-ph-y, -4%);
    width: var(--orn-tile-ph-w, 144%); height: var(--orn-tile-ph-h, 108%);
    /* Worked out from the tile's own skew rather than written again, so the
       photo can never lean the wrong way when the tile is flattened. */
    transform: skewX(calc(-1 * var(--orn-tile-skew, -8deg)));
    transition: transform .7s cubic-bezier(.2,.7,.3,1);
  }
  [data-site-template="apex"] .ax-tile-ph img { width: 100%; height: 100%; max-width: none; object-fit: cover; }
  [data-site-template="apex"] .ax-tile:hover .ax-tile-ph { transform: skewX(calc(-1 * var(--orn-tile-skew, -8deg))) scale(var(--orn-photo-zoom, 1.07)); }
  [data-site-template="apex"] .ax-tile figcaption {
    position: absolute; left: 0; bottom: 0;
    transform: skewX(calc(-1 * var(--orn-tile-skew, -8deg)));
    padding: 14px 20px;
    font-size: 10.5px; letter-spacing: 0.24em; text-transform: uppercase;
    font-weight: 600; color: #fff;
    text-shadow: 0 2px 14px rgba(0,0,0,.85);
  }
  /* Edit mode drops the skew so the click targets and the inline editor line up
     with what the owner sees. */
  [data-site-template="apex"] .ax-tiles.is-editing .ax-tile,
  [data-site-template="apex"] .ax-tiles.is-editing .ax-tile-ph,
  [data-site-template="apex"] .ax-tiles.is-editing .ax-tile figcaption { transform: none; }
  [data-site-template="apex"] .ax-tiles.is-editing .ax-tile-ph { left: 0; top: 0; width: 100%; height: 100%; }
  /* A spot the shop has asked for but not filled. EDIT MODE ONLY: the band drops
     these off-site, so this class never reaches a published page. Transparent
     because the add tile inside it carries its own surface and dashed outline. */
  [data-site-template="apex"] .ax-tile-empty { background: transparent; }

  /* ---- Recent work: the arrangements a shop can choose ----
     Same rule as the service cards: no attribute means the staggered band above,
     untouched. Both choices level the heights out.

     STRAIGHT also drops the skew, so the tiles read as a plain even run of
     photos. The photo layer has to be put back square at the same time, since it
     is oversized and counter-skewed purely to fill a parallelogram.
     MOSAIC keeps the skew and mixes the widths on the same six tracks the
     service cards use. */
  [data-site-template="apex"] .ax-tiles[data-sec-layout] .ax-tile:nth-child(odd),
  [data-site-template="apex"] .ax-tiles[data-sec-layout] .ax-tile:nth-child(even) {
    height: clamp(240px, 23vw, 340px);
    margin-top: 0;
  }
  [data-site-template="apex"] .ax-tiles[data-sec-layout="straight"] .ax-tile,
  [data-site-template="apex"] .ax-tiles[data-sec-layout="straight"] .ax-tile-ph,
  [data-site-template="apex"] .ax-tiles[data-sec-layout="straight"] .ax-tile figcaption { transform: none; }
  [data-site-template="apex"] .ax-tiles[data-sec-layout="straight"] .ax-tile-ph { left: 0; top: 0; width: 100%; height: 100%; }
  [data-site-template="apex"] .ax-tiles[data-sec-layout="straight"] .ax-tile:hover .ax-tile-ph { transform: scale(var(--orn-photo-zoom, 1.07)); }
  [data-site-template="apex"] .ax-tiles[data-sec-layout="mosaic"] { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  [data-site-template="apex"] .ax-tiles[data-sec-layout="mosaic"] .ax-tile { grid-column: span var(--ax-span, 2); }

  /* SHOWCASE is not a grid at all, so it does not go through .ax-tiles. It is a
     row of columns and brings its own container (ShowcaseCollage), which owns
     every measurement. All this has to do is stop the band styling its own tiles
     the way the staggered band needs: the two fixed heights, the drop on every
     other one, the skew, and the oversized counter-skewed photo layer that only
     exists to fill a parallelogram. Written after the rules above deliberately;
     they match on the same specificity and the later one has to win.
     THE CLASS NAME IS ITS OWN LESSON. This was ax-shot for an afternoon, which
     is already the Areas band's photo frame further down this file: a fixed 4/3.2
     box, a near-black background and a 16px radius. The collage inherited all
     three and photographed as one big dark rectangle. A band-specific name is
     cheaper than finding that twice.

     BOTH CLASSES ARE NAMED ON PURPOSE, and it is not belt and braces. The tile
     heights are set by rules of exactly this weight, so whichever is written
     LAST wins, and one of them lives in the phone block far below this one. That
     rule was forcing every tile to 290px on a phone: the big photo survived it,
     because a flex basis decides its height, but the two small ones take their
     height across the row and grew straight over the Show more button. Naming
     the root twice outweighs those rules wherever they sit in this file. */
  [data-site-template="apex"] .ax-work-collage.sc-collage .ax-tile { height: auto; margin-top: 0; transform: none; }
  [data-site-template="apex"] .ax-work-collage .ax-tile-ph {
    left: 0; top: 0; width: 100%; height: 100%;
    transform: none;
  }
  [data-site-template="apex"] .ax-work-collage .ax-tile:hover .ax-tile-ph { transform: scale(var(--orn-photo-zoom, 1.06)); }
  [data-site-template="apex"] .ax-work-collage .ax-tile figcaption { transform: none; }

  /* The per-tile edit controls. Only ever rendered in edit mode, and stacked at
     the top right so they clear the caption in the bottom left. */
  [data-site-template="apex"] .ax-tile-edit {
    position: absolute; top: 10px; right: 10px; z-index: 5;
    display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
  }
  [data-site-template="apex"] .ax-tile-btn {
    background: rgba(10,10,12,.72);
    border: none;
    color: #fff;
    border-radius: 8px;
    padding: 6px 10px;
    font: inherit; font-size: 11px; font-weight: 600;
    cursor: pointer;
    display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
    transition: background .15s ease, border-color .15s ease;
  }
  [data-site-template="apex"] .ax-tile-btn:hover { background: var(--site-accent); border-color: var(--site-accent); }

  /* =========================================================================
     REVIEWS
     ========================================================================= */
  [data-site-template="apex"] .ax-revs { display: grid; grid-template-columns: .85fr 1.15fr; gap: clamp(34px, 4.5vw, 64px); align-items: start; }
  [data-site-template="apex"] .ax-rating-rule { display: var(--orn-rating-rule, block); width: 88px; height: 2px; background: var(--site-accent); margin: 20px 0; }
  [data-site-template="apex"] .ax-rating b { font-family: var(--site-font-head); font-weight: 900; font-size: 66px; line-height: .9; display: inline-block; color: var(--site-fg); }
  [data-site-template="apex"] .ax-rating small { color: var(--site-muted); font-size: 15px; margin-left: 6px; }
  /* The whole right hand side of the rating panel arrangement: the reviews, the
     button that brings the rest in, and the edit-mode tail, as one grid cell. */
  [data-site-template="apex"] .ax-rev-col { min-width: 0; }
  [data-site-template="apex"] .ax-rev-list { display: grid; gap: 16px; }
  [data-site-template="apex"] .ax-rev {
    border: none;
    padding: 26px 28px;
    background: var(--site-panel);
    transition: transform .4s cubic-bezier(.2,.7,.3,1), border-color .4s ease;
  }
  [data-site-template="apex"] .ax-rev:hover { transform: translateY(-5px); border-color: var(--site-accent); }
  [data-site-template="apex"] .ax-rev p { color: var(--site-fg); font-size: 14.5px; margin: 0; line-height: 1.62; }
  [data-site-template="apex"] .ax-rev-who {
    margin-top: 15px;
    display: flex; align-items: center; gap: 12px;
    font-size: 11.5px; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--site-muted);
  }
  [data-site-template="apex"] .ax-rev-name { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
  /* THE ROUND SLOT. Google sends a small avatar and it is the only shape that
     survives one. A shop's own review uses whatever picture is on its record, in
     the same circle, so the two kinds of card do not read as two designs. */
  [data-site-template="apex"] .ax-rev-face {
    width: 38px; height: 38px; border-radius: 50%; flex: none;
    object-fit: cover; border: none;
    background: var(--site-bg-2);
  }
  [data-site-template="apex"] .ax-rev-initials {
    display: grid; place-items: center;
    font-family: var(--site-font-head); font-weight: 800;
    font-size: 13px; letter-spacing: 0.04em; color: var(--site-muted);
  }
  [data-site-template="apex"] .ax-rev-src {
    margin-top: 10px;
    font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--site-muted); opacity: .72;
  }
  [data-site-template="apex"] .ax-rev .ax-stars { margin-bottom: 13px; }

  /* ---- Reviews: the arrangements a shop can choose ----
     The rating column only belongs beside a narrow list of cards. The other
     four run full width with the heading above them, so the wrap is one
     column rather than two. */
  [data-site-template="apex"] .ax-revs-wide { display: grid; grid-template-columns: 1fr; gap: clamp(26px, 3.2vw, 44px); }
  /* The heading block was built to fill a narrow column beside the cards. Given
     the whole width it stretches its reading line right across the page and
     pushes the reviews below the fold, so it keeps a column's width. */
  [data-site-template="apex"] .ax-revs-wide .site-sec-body { max-width: 680px; }
  /* The turning disc says 4.9 out of 762, which is exactly what the number above
     it says. Beside a narrow column that repetition fills an empty corner; above
     a full-width row it is 140px of nothing between the heading and the reviews. */
  [data-site-template="apex"] .ax-revs-wide .ax-rev-stamp { display: none; }

  /* CAROUSEL. A scroll container first and arrows second, so it still works
     with a trackpad, a touch screen and a keyboard if the buttons never load. */
  [data-site-template="apex"] .ax-rev-car { position: relative; min-width: 0; }
  [data-site-template="apex"] .ax-rev-rail {
    display: flex; gap: 18px;
    overflow-x: auto; scroll-snap-type: x mandatory;
    padding-bottom: 6px;
    /* WITHOUT THIS THE RAIL DOES NOT SCROLL, IT WIDENS. A grid and flex item
       will not shrink below its content unless told, so five 340px cards made a
       926px rail inside a 390px phone and the whole PAGE scrolled sideways
       instead of the row. overflow-x on its own does not fix that. */
    min-width: 0;
  }
  [data-site-template="apex"] .ax-rev-rail .ax-rev { flex: 0 0 clamp(260px, 30%, 380px); scroll-snap-align: start; }
  /* The lift the cards do everywhere else fights a scroller, because the card
     under the pointer moves out from under it as the row slides. */
  [data-site-template="apex"] .ax-rev-rail .ax-rev:hover, [data-site-template="apex"] .ax-rev-track .ax-rev:hover { transform: none; }
  [data-site-template="apex"] .ax-rev-arrows { display: flex; gap: 10px; margin-top: 18px; }
  [data-site-template="apex"] .ax-rev-arrow {
    width: 42px; height: 42px; border-radius: 50%;
    border: none; background: transparent;
    color: var(--site-fg); display: grid; place-items: center; cursor: pointer;
    transition: border-color .3s ease, color .3s ease;
  }
  [data-site-template="apex"] .ax-rev-arrow:hover { border-color: var(--site-accent); color: var(--site-accent); }

  /* AUTO-SCROLL. See the component for why there are two sets. */
  /* Same reason as the rail: it has to be allowed to be narrower than the row
     inside it, or it pushes the page wide rather than clipping. */
  [data-site-template="apex"] .ax-rev-ticker { overflow: hidden; min-width: 0; }
  [data-site-template="apex"] .ax-rev-track {
    display: flex; gap: 18px; width: max-content;
    animation: ax-rev-roll var(--ax-rev-secs, 30s) linear infinite;
  }
  [data-site-template="apex"] .ax-rev-set { display: flex; gap: 18px; }
  [data-site-template="apex"] .ax-rev-track .ax-rev { flex: 0 0 clamp(260px, 26vw, 360px); }
  /* Reading a review that is moving is not possible, so it stops for anyone
     who reaches for it, by pointer or by keyboard. */
  [data-site-template="apex"] .ax-rev-ticker:hover .ax-rev-track,
  [data-site-template="apex"] .ax-rev-track:focus-within { animation-play-state: paused; }
  @keyframes ax-rev-roll {
    from { transform: translateX(0); }
    to { transform: translateX(calc(-50% - 9px)); }
  }

  /* READ MORE. The clamp is a DISPLAY limit and nothing else: the whole review
     is in the page either way, which is what keeps it inside Google's terms. */
  [data-site-template="apex"] .ax-rev.is-clamped p {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: var(--ax-rev-lines, 6);
    overflow: hidden;
  }
  [data-site-template="apex"] .ax-rev-more {
    align-self: flex-start;
    margin-top: 10px; padding: 0;
    background: none; border: 0; cursor: pointer;
    font-family: var(--site-font-head); font-weight: 700;
    font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
    color: var(--site-accent);
    border-bottom: 1px solid transparent;
  }
  [data-site-template="apex"] .ax-rev-more:hover { border-bottom-color: var(--site-accent); }

  /* SPOTLIGHT. The lead is NOT stretched to the column beside it: four reviews
     stacked two by two run past sixteen hundred pixels, and a lead card pulled
     out to match is a page of empty panel under one quote. */
  [data-site-template="apex"] .ax-rev-spot { display: grid; grid-template-columns: 1.3fr 1fr; gap: 18px; align-items: start; min-width: 0; }
  [data-site-template="apex"] .ax-rev-spot > * { min-width: 0; }
  [data-site-template="apex"] .ax-rev-spot-lead { display: flex; }
  [data-site-template="apex"] .ax-rev-spot-lead .ax-rev { width: 100%; }
  [data-site-template="apex"] .ax-rev-spot-lead .ax-rev p { font-size: clamp(16px, 1.35vw, 19px); line-height: 1.58; }
  [data-site-template="apex"] .ax-rev-spot-rest { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-content: start; }

  /* STACKED. A reading measure, because the whole point is that it is read. */
  [data-site-template="apex"] .ax-rev-stack { display: grid; gap: 18px; max-width: 900px; min-width: 0; }
  [data-site-template="apex"] .ax-rev-stack .ax-rev { padding: 30px 34px; }
  [data-site-template="apex"] .ax-rev-stack .ax-rev p { font-size: clamp(15px, 1.2vw, 17.5px); line-height: 1.62; }

  /* The button that brings the rest of a column of reviews in. Full width so it
     reads as the end of the list rather than as another card. */
  [data-site-template="apex"] .ax-rev-showall {
    display: block; width: 100%;
    margin-top: 16px; padding: 15px 18px;
    background: transparent;
    border: none;
    color: var(--site-fg);
    font-family: var(--site-font-head); font-weight: 700;
    font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase;
    cursor: pointer;
    transition: border-color .3s ease, color .3s ease;
  }
  [data-site-template="apex"] .ax-rev-showall:hover { border-color: var(--site-accent); color: var(--site-accent); }
  [data-site-template="apex"] .ax-rev-stack .ax-rev-showall { max-width: 900px; }

  /* WHAT THE SHOP TOOK DOWN. Edit mode only, under whichever arrangement is
     drawn rather than inside it: a hidden review sliding past in the carousel
     would be worse than not showing it at all. */
  [data-site-template="apex"] .ax-rev-tail { margin-top: 28px; padding-top: 22px; border-top: 1px dashed var(--site-border); }
  [data-site-template="apex"] .ax-rev-tailhead {
    margin: 0 0 14px;
    font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase;
    color: var(--site-muted);
  }
  [data-site-template="apex"] .ax-rev-tailgrid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 16px; align-items: stretch;
  }
  [data-site-template="apex"] .ax-rev.is-away { opacity: .42; }
  [data-site-template="apex"] .ax-rev.is-away:hover { opacity: .85; transform: none; }
  [data-site-template="apex"] .ax-rev-edit { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
  /* The disc sits in the bottom corner of the reviews band, in the space the
     short left column leaves under its button. A box its own size, so the disc
     keeps the absolute positioning the collage gave it and lands in the corner
     of THIS box instead of the collage. */
  [data-site-template="apex"] .ax-rev-stamp { position: relative; width: 140px; height: 140px; margin-top: 44px; }
  /* Bigger here than on the collage, because the space it fills is bigger. The
     disc is one SVG on one viewBox, so its size is the box it is given. */
  [data-site-template="apex"] .ax-rev-stamp .ax-stamp { left: 0; bottom: 0; width: 100%; height: 100%; }

  /* =========================================================================
     SERVICE AREAS
     ========================================================================= */
  [data-site-template="apex"] .ax-towns { display: grid; grid-template-columns: 1fr 1fr; gap: 3px 28px; margin: 22px 0 28px; max-width: 520px; padding: 0; }
  [data-site-template="apex"] .ax-towns li { list-style: none; display: flex; align-items: center; gap: 10px; font-size: 14.5px; color: var(--site-muted); padding: 5px 0; }
  [data-site-template="apex"] .ax-towns i { display: var(--orn-town-bullet-display, block); width: 6px; height: 6px; border-radius: var(--orn-town-bullet-radius, 0); background: var(--site-accent); transform: var(--orn-town-bullet-rotate, rotate(45deg)); flex: none; }
  /* The add and remove controls. EDIT MODE ONLY: the band renders none of this
     off-site, so none of these class names can reach a published page. */
  [data-site-template="apex"] .ax-town-x {
    width: 18px; height: 18px; border: 0; border-radius: 9px; padding: 0;
    background: transparent; color: var(--site-subtle); cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    opacity: 0; transition: opacity .15s ease, background .15s ease, color .15s ease;
  }
  [data-site-template="apex"] .ax-towns li:hover .ax-town-x { opacity: 1; }
  [data-site-template="apex"] .ax-town-x:hover { background: var(--site-accent); color: var(--site-btn-fg, #fff); }
  [data-site-template="apex"] .ax-town-x:disabled { cursor: wait; }
  [data-site-template="apex"] .ax-town-add { margin: 0 0 26px; max-width: 520px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
  [data-site-template="apex"] .ax-town-add-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  [data-site-template="apex"] .ax-town-add-btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 9px 16px; background: transparent; border: 2px dashed currentColor;
    font-family: var(--site-font-head); font-size: 13px; font-weight: 600;
    letter-spacing: 0.04em; text-transform: uppercase; cursor: pointer;
  }
  [data-site-template="apex"] .ax-town-add-btn:disabled { opacity: .5; cursor: not-allowed; }
  [data-site-template="apex"] .ax-town-cancel {
    padding: 9px 12px; background: transparent; border: 0;
    color: var(--site-muted); font: inherit; font-size: 13px; cursor: pointer;
  }
  [data-site-template="apex"] .ax-town-input {
    flex: 1 1 180px; min-width: 0;
    padding: 9px 12px; background: var(--site-surface); color: var(--site-fg);
    border: none; border-left-width: 2px;
    font: inherit; font-size: 14.5px;
  }
  [data-site-template="apex"] .ax-town-hint { margin: 8px 0 0; font-size: 12px; color: var(--site-muted); }
  /* The headings a shop has, listed above its towns while it is editing, so the
     Add heading button has a visible result. Edit mode only: the published page
     draws headings above the towns they group, not as a row of chips. */
  [data-site-template="apex"] .ax-town-heads { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
  [data-site-template="apex"] .ax-town-head-chip {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 6px 8px 6px 12px;
    border: none;
    background: var(--site-surface);
    font-family: var(--site-font-head);
    font-size: 12px; font-weight: 700; letter-spacing: 0.16em;
    text-transform: uppercase; color: var(--site-fg);
  }
  [data-site-template="apex"] .ax-town-head-chip button {
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; background: transparent; color: var(--site-muted);
    cursor: pointer; padding: 2px;
  }
  [data-site-template="apex"] .ax-town-head-chip button:hover { color: var(--site-accent); }
  /* The group picker beside the add field, drawn only when a shop has headings. */
  [data-site-template="apex"] .ax-town-group {
    padding: 10px 12px;
    background: var(--site-surface);
    border: none;
    color: var(--site-fg);
    font-family: inherit; font-size: 14px;
  }
  [data-site-template="apex"] .ax-shot { position: relative; overflow: hidden; aspect-ratio: 4 / 3.2; background: #111; border-radius: 16px; }
  [data-site-template="apex"] .ax-shot img { width: 100%; height: 100%; object-fit: cover; }

  /* =========================================================================
     ACCENT CTA BAND
     Angled on BOTH edges, so it reads as a slab cut out of the page. The cut
     is written here rather than through data-sec-cut because that control only
     does one edge at a time.
     ========================================================================= */
  [data-site-template="apex"] .ax-cta.site-sec {
    background: var(--site-accent);
    color: var(--site-btn-fg, #fff);
    clip-path: polygon(0 40px, 100% 0, 100% calc(100% - 40px), 0 100%);
    padding: clamp(84px, 7.4vw, 120px) 0;
    overflow: hidden;
  }
  [data-site-template="apex"] .ax-cta h2 {
    max-width: 15ch;
    margin: 0;
    color: var(--site-btn-fg, #fff);
    font-size: clamp(28px, 3.2vw, 46px);
  }
  [data-site-template="apex"] .ax-cta p { color: var(--site-btn-fg, #fff); opacity: .88; max-width: 42ch; margin: 14px 0 0; font-weight: 500; }
  [data-site-template="apex"] .ax-cta-in { display: flex; align-items: center; justify-content: space-between; gap: 40px; flex-wrap: wrap; }
  [data-site-template="apex"] .ax-cta-btns { display: flex; gap: 12px; flex-wrap: wrap; }
  /* The ghost word on the accent slab. It sits on a saturated fill rather than on
     black or white, so the whisper that reads on a dark band disappears here and
     it needs its own value. Coloured from the button-label token, which is already
     the contrast colour for whatever accent the shop set, so a pale accent gets a
     dark ghost instead of an invisible white one. */
  [data-site-template="apex"] [data-sec-backdrop="mark"] .ax-cta.site-sec::before {
    color: var(--site-btn-fg, #fff);
    opacity: 0.12;
  }

  /* A soft white bloom rather than any texture. */
  [data-site-template="apex"] .ax-cta-bloom {
    position: absolute; inset: -30% -10%;
    z-index: 0; pointer-events: none;
    background: radial-gradient(44% 48% at 84% 24%, rgba(255,255,255,.16), transparent 66%);
  }
  [data-site-template="apex"] .ax-cta .site-wrap { position: relative; z-index: 2; }

  /* =========================================================================
     RESPONSIVE
     ========================================================================= */
  /* ---- READABLE ON A PHONE ----
     Sixty five percent of this shop's real traffic is on a phone, and the site
     had never been looked at on one. Measured at 390px wide: the small labels
     on this page render at 10 to 11 pixels, which is the size of the small
     print on the back of a bottle. They are fine on a desktop, where a 10px
     letter-spaced kicker above a heading is a deliberate design choice, but
     nobody ever stepped them up for a small screen.

     This only ever raises, never shrinks. Every class named here was measured
     below 12px on a phone and is lifted to 12px, which is the floor for
     uppercase letter-spaced text. Desktop is untouched, and so is any shop's
     own choice: this is a size floor, not a redesign. */
  @media (max-width: 600px) {
    [data-site-template="apex"] .ax-card-kicker,
    [data-site-template="apex"] .ax-card-cta-t,
    [data-site-template="apex"] .ax-rev-src,
    [data-site-template="apex"] .ax-rev-more,
    [data-site-template="apex"] .site-eyebrow { font-size: 12px; }
  }

  /* ---- A CARD TITLE USES ITS CARD ON A PHONE ----
     The 13ch cap is what keeps a title a tidy two lines in a quarter width card
     on a desktop. On a phone the card is the whole screen wide, 320 to 350px,
     and the same cap held the title to 144px down the middle of it: a long
     service name ran to five lines with empty photo either side. */
  @media (max-width: 700px) {
    [data-site-template="apex"] .ax-card h3 { max-width: none; }
  }

  @media (max-width: 1100px) {
    [data-site-template="apex"] .ax-svc, [data-site-template="apex"] .ax-films { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    [data-site-template="apex"] .ax-card:nth-child(even) { margin-top: 0; }
    /* minmax(0, 1fr), not 1fr. A plain 1fr is minmax(auto, 1fr), and that auto
       floor is the column's own min-content, so a wide child holds the column
       open and the band runs off the side of the screen. Measured on a 390px
       phone: this column was 377.86px inside a 350px content box, which put the
       words of the "why" band 8px past the right edge with the last letter of
       every line clipped. Letting it shrink to zero is the standard fix and it
       can only ever ALLOW shrinking, so nothing that already fitted moves. */
    [data-site-template="apex"] .ax-split, [data-site-template="apex"] .ax-shade, [data-site-template="apex"] .ax-revs { grid-template-columns: minmax(0, 1fr); }
    /* And the items themselves. A grid item's default min-width is auto, which
       is its own min-content, so letting the COLUMN shrink is only half of it:
       the item sat at 377.86px inside a 350px column and still hung 8px off the
       screen. These two lines always travel together. */
    [data-site-template="apex"] .ax-split > *, [data-site-template="apex"] .ax-shade > *, [data-site-template="apex"] .ax-revs > * { min-width: 0; }
    /* The heading block asks for a 420px floor, which is wider than the phone it
       is being drawn on, so it held the whole band 8px past the right edge and
       clipped the last letter of every line of copy under it. On a screen this
       narrow the floor has to be the screen. */
    [data-site-template="apex"] .ax-head-main { min-width: 0; }
    [data-site-template="apex"] .ax-rev-spot { grid-template-columns: 1fr; }
    [data-site-template="apex"] .ax-rev-rail .ax-rev { flex-basis: clamp(240px, 46%, 340px); }
    [data-site-template="apex"] .ax-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px; }
    [data-site-template="apex"] .ax-steps::before { display: none; }
    [data-site-template="apex"] .ax-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    [data-site-template="apex"] .ax-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    [data-site-template="apex"] .ax-stat:nth-child(2n) { border-right: 0; }
    [data-site-template="apex"] .ax-stat { border-bottom: 1px solid rgba(255,255,255,.12); }
    [data-site-template="apex"] .ax-side { display: none; }
    [data-site-template="apex"] .ax-collage { --ax-collage-h: 430px; }
    /* The taller compositions come down with everything else, or a shop that
       asked for six would hand a tablet a band twice the height of its words. */
    [data-site-template="apex"] .ax-collage[data-count="4"] { --ax-collage-h: 480px; }
    [data-site-template="apex"] .ax-collage[data-count="5"] { --ax-collage-h: 515px; }
    [data-site-template="apex"] .ax-collage[data-count="6"] { --ax-collage-h: 550px; }
    /* The collage has the whole width to itself here and half the height, so it
       gives a little of the spacing back rather than pushing the photos flat. */
    [data-site-template="apex"] .ax-collage[data-sec-layout="feature"] { --ax-collage-gap: 18px; }
    [data-site-template="apex"] .ax-film-cols-3 { --ax-film-cols: 2; }
    /* A chosen arrangement collapses exactly like the default one does. Stated
       again here because the [data-sec-layout] rules above outrank a plain class
       selector, so without this a mosaic would try to hold six tracks on a
       tablet. Everything goes back to two even columns. */
    [data-site-template="apex"] .ax-svc[data-sec-layout], [data-site-template="apex"] .ax-tiles[data-sec-layout] {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    [data-site-template="apex"] .ax-svc[data-sec-layout] .ax-card,
    [data-site-template="apex"] .ax-tiles[data-sec-layout] .ax-tile { grid-column: auto; }
    [data-site-template="apex"] .ax-svc[data-sec-layout="mosaic"] .ax-card { height: auto; aspect-ratio: 1 / 1.4; }
    /* An odd run of photos would otherwise leave the last one alone in the left
       column with a hole beside it. It takes the whole row instead. */
    [data-site-template="apex"] .ax-tiles[data-sec-fill="odd"] .ax-tile:last-child { grid-column: span 2; }

    /* Chosen hero arrangements. Left and right stop meaning anything on a narrow
       screen, so the photo goes above the words at a height that still reads as a
       photo rather than as a band, and the two-column type layouts fold. */
    [data-site-template="apex"] .ax-hero[data-hero-layout="split"] {
      grid-template-columns: minmax(0, 1fr);
      grid-template-rows: auto auto;
    }
    [data-site-template="apex"] .ax-hero[data-hero-layout="split"] .ax-hero-media {
      grid-column: 1; grid-row: 1; min-height: 0; height: clamp(210px, 40vw, 330px);
    }
    [data-site-template="apex"] .ax-hero[data-hero-layout="split"] .ax-hero-inner {
      grid-column: 1; grid-row: 2;
      padding-left: var(--site-gutter); padding-right: var(--site-gutter);
      /* Stacked, the band's own minimum height would leave a hole between the
         photo and the headline, because the words are still being centred in a
         column that is now most of the screen. */
      justify-content: flex-start;
    }
    [data-site-template="apex"] .ax-hero[data-hero-layout="split"] { min-height: 0; }
    [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .ax-hero-copy { grid-template-columns: minmax(0, 1fr); }
    [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .ax-hero-sub { grid-column: 1; margin-top: 18px; }
    [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] .ax-hero-btns {
      grid-column: 1; grid-row: auto; margin-top: 22px;
    }
  }
  /* CENTRED, ON A TABLET. Between 681 and 1100px these bands are two columns, and
     the centred-row tracks above only apply wider than that, so a centred band
     with an odd number of cards left its last card alone on the left. That card
     now sits in the middle of its row at the width of one column, which is what
     the shop's Centre means at this width. Left or right aligned bands are
     untouched. Measured on test shop 2, 2026-09-14. */
  @media (min-width: 681px) and (max-width: 1100px) {
    [data-site-template="apex"] .ax-svc[data-center-row] > :last-child:nth-child(odd) {
      grid-column: 1 / -1;
      justify-self: center;
      width: calc(50% - 10px);
    }
    [data-site-template="apex"] .ax-tiles[data-center-row] > :last-child:nth-child(odd),
    [data-site-template="apex"] .ax-films[data-center-row] > :last-child:nth-child(odd) {
      grid-column: 1 / -1;
      justify-self: center;
      width: calc(50% - 9px);
    }
  }
  @media (max-width: 680px) {
    [data-site-template="apex"] .ax-svc, [data-site-template="apex"] .ax-tiles, [data-site-template="apex"] .ax-films { grid-template-columns: 1fr; }
    /* One column wide, a card the shop asked to be tall would be most of a
       screen on its own. Only the tall choice is pulled back, and only here. */
    [data-site-template="apex"] .ax-svc[data-card-size="large"] .ax-card { aspect-ratio: 1 / 1.4; }
    [data-site-template="apex"] .ax-tile:nth-child(odd), [data-site-template="apex"] .ax-tile:nth-child(even) { height: 290px; margin-top: 0; }
    [data-site-template="apex"] .ax-svc[data-sec-layout], [data-site-template="apex"] .ax-tiles[data-sec-layout] { grid-template-columns: 1fr; }
    [data-site-template="apex"] .ax-tiles[data-sec-layout] .ax-tile:nth-child(odd),
    [data-site-template="apex"] .ax-tiles[data-sec-layout] .ax-tile:nth-child(even) { height: 290px; }
    /* One column, so there is no stray last photo to widen. Without this the
       tablet rule above still asks for two tracks and the grid grows a second
       column of nothing, which squeezes every other photo to zero. */
    [data-site-template="apex"] .ax-tiles[data-sec-fill="odd"] .ax-tile:last-child { grid-column: auto; }
    [data-site-template="apex"] .ax-towns { grid-template-columns: 1fr; }
    /* On a phone the hero is content height, so there is no screen to fill and
       the only thing between the nav and the headline is dead photo. Trimmed so
       the headline, the button and the stat row all start higher and the page
       gets shorter rather than longer. */
    [data-site-template="apex"] .ax-hero { min-height: auto; padding-top: 72px; }
    [data-site-template="apex"] .ax-specs { grid-template-columns: 1fr; }
    [data-site-template="apex"] .ax-spec { border-right: 0; border-bottom: 1px solid var(--site-border); }
    [data-site-template="apex"] .ax-spec:last-child { border-bottom: 0; }
    [data-site-template="apex"] .ax-stats { grid-template-columns: 1fr; }
    [data-site-template="apex"] .ax-stat { border-right: 0; }
    [data-site-template="apex"] .ax-marquee { margin: 24px 0 -10px; }
    [data-site-template="apex"] .ax-collage { --ax-collage-h: 340px; }
    [data-site-template="apex"] .ax-collage[data-count="4"] { --ax-collage-h: 400px; }
    [data-site-template="apex"] .ax-collage[data-count="5"] { --ax-collage-h: 440px; }
    [data-site-template="apex"] .ax-collage[data-count="6"] { --ax-collage-h: 480px; }
    /* A phone has a third of the width to share out, so the same spacing that
       reads as deliberate on a desktop would eat the photos here. */
    [data-site-template="apex"] .ax-collage[data-sec-layout="feature"] { --ax-collage-gap: 10px; }
    /* The steps stay two across on a phone (as the design does), so the numeral
       has to give ground or an uppercase title would wrap to three or four lines
       in what is left of the column. Reserve and numeral shrink together, so they
       still cannot touch. */
    [data-site-template="apex"] .ax-step { --ax-step-no-w: 40px; padding-top: 48px; }
    [data-site-template="apex"] .ax-step-no { font-size: 36px; top: 38px; }
    [data-site-template="apex"] .ax-cta.site-sec { clip-path: polygon(0 22px, 100% 0, 100% calc(100% - 22px), 0 100%); }
    [data-site-template="apex"] .ax-head { margin-bottom: 34px; }

    /* THE NUMBERS STAY TWO ACROSS ON A PHONE, they do not go one per row.
       A shop with four numbers gets a block half as tall, which is the thing
       that pushes the rest of the first screen out of sight. Applied to the
       CHOSEN arrangements only, so the shipped hero is left exactly as it is and
       nothing any shop is looking at today moves. */
    /* --ax-stat-cols-sm is the shop's real number of stats capped at two, worked
       out by the component beside the desktop count, so ONE stat still takes the
       whole width instead of sitting in half a panel with a hole beside it. */
    [data-site-template="apex"] .ax-hero[data-hero-layout] .ax-stats {
      grid-template-columns: repeat(var(--ax-stat-cols-sm, 2), minmax(0, 1fr));
    }
    [data-site-template="apex"] .ax-hero[data-hero-layout] .ax-stat {
      border-right: 1px solid rgba(255,255,255,.12);
      padding: 18px 16px;
    }
    [data-site-template="apex"] .ax-hero[data-hero-layout] .ax-stat:nth-child(2n) { border-right: 0; }
    [data-site-template="apex"] .ax-hero[data-hero-layout="compact"] { min-height: 0; }
    [data-site-template="apex"] .ax-hero[data-hero-layout="panel"] .ax-hero-copy { padding: 24px 20px; }
  }

  /* =========================================================================
     MOTION
     ========================================================================= */
  @media (prefers-reduced-motion: reduce) {
    [data-site-template="apex"] .ax-rv { opacity: 1; translate: none; transition: none; }
    [data-site-template="apex"] .ax-hero-glow, [data-site-template="apex"] .ax-mq-track, [data-site-template="apex"] .ax-stamp-spin { animation: none; }
    [data-site-template="apex"] .ax-card, [data-site-template="apex"] .ax-card-ph img, [data-site-template="apex"] .ax-film, [data-site-template="apex"] .ax-tile img,
    [data-site-template="apex"] .ax-rev, [data-site-template="apex"] .ax-meter-fill, [data-site-template="apex"] .ax-viewport-film, [data-site-template="apex"] .ax-card-pip,
    [data-site-template="apex"] .ax-card-cta, [data-site-template="apex"] .ax-card-cta-a { transition: none; }
    [data-site-template="apex"] .ax-card:hover, [data-site-template="apex"] .ax-film:hover, [data-site-template="apex"] .ax-rev:hover { transform: none; }
    [data-site-template="apex"] .ax-card:hover .ax-card-ph img { transform: none; }
    /* Worked out from the tile's own lean rather than written again. Fixed at
       8 degrees this tilted the photo on hover for a look that had squared the
       tiles off, which is the one visitor group that asked for no movement. */
    [data-site-template="apex"] .ax-tile:hover img { transform: skewX(calc(-1 * var(--orn-tile-skew, -8deg))); }
    /* That counter-skew exists to sit straight inside a parallelogram, and the
       collage has none. Left alone it tilts the photo on hover for exactly the
       visitors who asked for no motion. */
    [data-site-template="apex"] .ax-work-collage .ax-tile:hover img { transform: none; }
    /* The moving row stops, and becomes a row you scroll yourself, so every
       review is still reachable rather than frozen off the side. */
    [data-site-template="apex"] .ax-rev-track { animation: none; width: auto; }
    [data-site-template="apex"] .ax-rev-ticker { overflow-x: auto; }
    [data-site-template="apex"] .ax-rev-set:last-child { display: none; }
  }


  /* =========================================================================
     ANGLES (site-wide)

     One setting in the Site Style panel tilts every angled device on the site
     together. Under Apex that is four things, and they all lean the same way
     today (everything rises to the right):

       the highlight strip          .site-marquee-strip
       the slanted band seams       [data-sec-cut="top" | "bottom"]
       the angled call to action    .ax-cta
       the slanted work tiles       .ax-tile and its counter-skewed layers

     Three states. No attribute is the existing lean, so a shop that has never
     opened the control renders byte-identically. data-angle="reverse" flips
     every one of them together, and data-angle="straight" levels them all off.

     TWO THINGS TO KNOW BEFORE EDITING THIS BLOCK.

     1. The selectors deliberately do NOT also test [data-site-template="apex"].
        On the public site both attributes sit on the same .site-root element, but
        in edit mode data-site-template is written onto .site-root while the Site
        Style attributes sit on the wrapper below it, so a compound selector would
        never match while the owner is previewing a choice. Every class named here
        is only ever emitted by an Apex section, so scoping by class alone reaches
        exactly the same elements and previews correctly too.

     2. This block must stay LAST in APEX_CSS. Several of these rules tie with the
        base rule on specificity and are resolved by source order.
     ========================================================================= */

  /* ---- The highlight strip ----
     scale(1.05) exists only to run the ends of the strip off both edges of a
     tilted bar, so Straight drops the over-scale along with the tilt and the
     strip sits flush at exactly the page width. */
  /* DECLARED ON THE ELEMENT THE ATTRIBUTE IS ON, NOT ON THE THING BEING DRAWN.
     data-angle sits on the site root, and so do the ornament variables, which
     are written there as an inline style. A custom property declared on the
     STRIP would beat a value inherited from the root, so writing it there let
     the site's lean silently override a theme that had asked for a level strip.
     Declared here, on the same element, the inline value wins, which is the
     order a person would expect: the theme's own choice beats the default that
     comes with the lean. */
  [data-angle="reverse"] { --orn-strip-tilt: 1.1deg; }
  [data-angle="straight"] { --orn-strip-tilt: 0deg; --orn-strip-scale: 1; }

  /* ---- The slanted band seams ----
     Straight removes the clip entirely, which is what leaves two bands meeting
     on a level edge rather than on a slant. */
  [data-angle="reverse"] [data-sec-cut="top"] .site-sec {
    clip-path: polygon(0 0, 100% 44px, 100% 100%, 0 100%);
  }
  [data-angle="reverse"] [data-sec-cut="bottom"] .site-sec {
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 44px));
  }
  [data-angle="straight"] [data-sec-cut="top"] .site-sec,
  [data-angle="straight"] [data-sec-cut="bottom"] .site-sec {
    clip-path: none;
  }

  /* ---- The angled call to action slab ----
     Cut on both edges, so a flip has to move both. */
  [data-angle="reverse"] .ax-cta.site-sec {
    clip-path: polygon(0 0, 100% 40px, 100% 100%, 0 calc(100% - 40px));
  }
  [data-angle="straight"] .ax-cta.site-sec { clip-path: none; }

  /* ---- The slanted work tiles ----
     The photo layer and the caption are counter-skewed so the parallelogram is
     filled edge to edge and the words stay upright, so both flip with the tile.
     Straight squares the tile off and puts the photo back on the box, which is
     exactly what edit mode already does to make the tiles clickable. */
  /* The lean is one variable now, so reversing or straightening the site only
     has to change the number. The photo and the caption counter-skew off the
     same value, which is what stops them ever leaning the wrong way. */
  [data-angle="reverse"] { --orn-tile-skew: 8deg; }
  [data-angle="straight"] { --orn-tile-skew: 0deg; }
  /* A FLAT TILE DOES NOT NEED THE OVERSIZED PHOTO. The photo box is blown up and
     offset only so a parallelogram is filled edge to edge; left on a square tile
     it crops the picture for no reason. Set as variables rather than as
     properties so the ornament switch, which flattens the tile without touching
     the site's lean setting, gets the same correction from the same place. The hover is left to
     the base rule, which already reads the skew and the zoom from variables. */
  [data-angle="straight"] {
    --orn-tile-ph-x: 0; --orn-tile-ph-y: 0;
    --orn-tile-ph-w: 100%; --orn-tile-ph-h: 100%;
  }

  /* The phone-width band seams and call to action carry shallower cuts, so the
     flipped and levelled versions need their own shallow copies. */
  @media (max-width: 600px) {
    [data-angle="reverse"] [data-sec-cut="top"] .site-sec {
      clip-path: polygon(0 0, 100% 22px, 100% 100%, 0 100%);
    }
    [data-angle="reverse"] [data-sec-cut="bottom"] .site-sec {
      clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 22px));
    }
    [data-angle="straight"] [data-sec-cut="top"] .site-sec,
    [data-angle="straight"] [data-sec-cut="bottom"] .site-sec {
      clip-path: none;
    }
    [data-angle="reverse"] .ax-cta.site-sec {
      clip-path: polygon(0 0, 100% 22px, 100% 100%, 0 calc(100% - 22px));
    }
    [data-angle="straight"] .ax-cta.site-sec { clip-path: none; }
  }

  /* Reduced motion swaps the tile hover for a plain skew, so that copy of the
     angle has to follow the setting too. */
  /* The reduced-motion counter-skew on a hovered tile photo lives in
     APEX_HOME_CSS beside the rule it corrects, because a copy written here
     carries one attribute selector fewer and loses to it every time. */
