/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   custom.css â€” THIS CLIENT'S bespoke design layer (Layer 3)
   â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   Per-client. Lives in the cloned Template (the client theme), NOT in the
   shared engine plugin. The engine loads it LAST â€” after primitives.css,
   tokens.css, base.css and the recipe-derived tokens â€” so anything here
   extends or overrides the shared `.hzo-*` components for THIS site only.
   Ships with `hozio-studio theme deploy`.

   â”€â”€ â›” THE SEED BELOW IS THE DESIGN CONTRACT. DO NOT DELETE IT. â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Everything from "THE CONTRACT, IN TOKENS" down is not example code and it
   is not a starting suggestion. It is
   `Hozio-Studio-Playbook/design-system/design-contract.md` expressed in CSS â€”
   the one page of literal numbers that outranks every other design document
   in the fleet, including this site's own DESIGN.md.

   EXTEND IT. RETUNE ITS VALUES. NEVER STRIP IT OUT AND START OVER.
   Deleting the seed does not give a build a clean slate; it gives it the
   engine's 2018 defaults (12px radius, a hairline shadow, border-and-shadow
   on one object) and a fresh obligation to re-derive the contract privately.
   That is exactly what happened before this file existed: one audited client
   re-declared 381 custom properties across 834 declarations, under its own
   prefix, and the next client re-declared them differently. The seed exists
   so a new site STARTS at the contract instead of arriving at it.

   Retuning a value is normal and expected â€” that is what the tokens are for.
   Removing a rule needs a reason written on the line.

   â”€â”€ THE CLIENT PREFIX â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   DONE for this client. The seed shipped under the placeholder `.cl-` and was
   renamed to `.sjm-` on 2026-09-04, at §3.1f, before any page or treatment
   sketch was built. 82 selectors moved. The command that did it, recorded so
   the next clone still has it (note the source pattern is the placeholder,
   not this site's prefix):

       (Get-Content assets/css/custom.css) -replace '[.]cl-', '.xx-' |
           Set-Content assets/css/custom.css -Encoding utf8

   â€¦and use the same prefix for every class this site adds afterwards. Never
   `.hzo-*` â€” that is the shared engine namespace and this file never
   redefines it. Setting an engine *variable* on an engine element is a
   different thing and is the sanctioned override route (styling-layers.md);
   the seed uses it once, in THE BINDING, below.

   â”€â”€ WHAT THIS FILE HOLDS: two layers, two budgets â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   Exactly TWO of the five routing layers (page-build.md Â§"THE ROUTING
   TABLE"). Nothing else is allowed in here.

   1. COMPONENT TREATMENT â€” the per-site FORM of a signature component
      family, decided once site-wide in docs/component-treatment.md.
      Presentation only: never markup contracts (order, tags, roles, ARIA,
      data-hzo-*, focus order). BUDGET: 350 lines, counted by marker.
      Every block MUST be opened by an `@treatment <family>` marker comment
      and closed by an `@end-treatment` one â€” both written as ordinary CSS
      block comments â€” or gate T3 cannot see it and the family reads as
      untreated; T3 wants >= 15 declarations inside the marker. One marker per
      family: nav/header, cards, cta, steps, pricing, testimonial, faq, stats.
      (Those two markers are spelled out literally, in comment form, at the
      first @treatment block a build adds â€” they are NOT written here,
      because a comment terminator inside a comment ends it early.)

   2. SITE SKIN â€” this client's token values plus site-wide client-prefixed
      decisions that recur on >= 2 page classes. BUDGET: 450 lines.
      Treatment + skin <= 800 total. Above that the build STOPS and the excess
      is justified as an engine gap, a treatment, or a signature.
      THE SEED COUNTS AS SKIN, and it is deliberately small so it leaves room.
      If the skin is at budget, the answer is never to delete the seed.

   â”€â”€ â›” SIGNATURE MOMENTS ARE **NOT** IN THIS FILE â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   A page's signature moment lives in the `_hzostudio_page_custom_css` /
   `_hzostudio_page_custom_js` PAGE META, namespaced under its
   docs/signature-ledger.md `id` (versioned source: docs/page-code/<slug>.css).
   Gate S1 reads that meta: a signature declared in the ledger but implemented
   here FAILS, because the gate looks in the meta and finds nothing. Likewise
   not here: anything the spec vocabulary already composes, and raw section
   markup (that is the spec's {"raw": â€¦} with a reason).

   â”€â”€ Rules of the road â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
   1. REUSE FIRST. Compose from the shared `.hzo-*` components before writing
      anything here. Only add CSS for the bespoke *delta* a section needs.
   2. CLIENT PREFIX on every new class (above). Page-scope page-specific
      rules so one page's CSS never bleeds onto another.
   3. USE TOKENS. Colour, space, radius, shadow and type come from the engine
      variables. A raw px or hex is for things the token system genuinely does
      not describe â€” a bespoke gradient, an aspect ratio, a physics constant.
   4. INVOKE THE DESIGN SKILLS. New CSS here is written WITH the design-skill
      stack (`hozio-design` routes it), never from memory.
   5. NEW CLIENT-SITE FILES LIVE ONLY IN THIS REPO â€” never the engine plugin
      or the Playbook.
   â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */

/* â•â•â• ENGINE-ROLE POLYFILL â€” DELETE WHEN THE FLEET MINIMUM CARRIES IT â•â•â•
   Every custom property below is an ENGINE role, not a client invention. Each
   one is declared here at EXACTLY the value the engine declares it at on
   `Hozio-Studio @ feat/wave-t-composition` (assets/css/tokens.css, see
   assets/css/TOKENS-MIGRATION.md Â§â€œDepth rolesâ€ / â€œRhythm + rail rolesâ€ /
   â€œType rolesâ€ / â€œ--radius-4 + --radius-objectâ€).

   WHY THIS EXISTS. The contract seed below READS these roles. They ship on
   that branch and NOWHERE ELSE: the released engine the fleet actually runs
   (v1.11.5 â€” the current fleet minimum) declares none of them. A `var()` with
   no fallback naming an undefined property invalidates the WHOLE declaration
   at computed-value time, so without this block the seed deploys as
   radius 0, padding 0 and no shadow â€” silently, with every gate GREEN.

   HARMLESS ONCE THE ENGINE SHIPS THEM. The engine declares its tokens inside
   `@layer tokens.base`; this file is unlayered, so this block wins â€” but it
   wins with the SAME value, which is the whole point. Identical to engine
   >= the first release carrying feat/wave-t-composition (unreleased as of
   2026-08-28). DELETE THIS BLOCK the day the fleet minimum engine carries
   these roles; nothing else in the file changes.

   NO ENGINE CONSUMER AT v1.11.5: --section-pad-y / --rail-gutter are read by
   nothing the engine ships â€” base.css and container.css still hardcode those
   ladders â€” so the contract's one-line rhythm retune below would be inert on
   its own. THE SECTION ROOT block below gives --section-pad-y a consumer
   (`.sjm-sec`), which is what makes that retune real; --rail-gutter is still
   spent only by the client's own container selector, so mirror the ladder,
   never flatten it. The day the engine ships its own consumers they agree.
   Same open question for --frame-radius in THE BINDING.
   â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */
:root {
    /* SPACING â€” the three numeric steps (tokens.css L86-88). */
    --space-32: 32px;
    --space-40: 40px;
    --space-56: 56px;

    /* RADIUS â€” the surface rung and its role name (tokens.css L180, L520).
       --radius-pill / --radius-control already exist at v1.11.5; not polyfilled. */
    --radius-4:      24px;
    --radius-object: var(--radius-4);

    /* DEPTH â€” the ink-tinted ladder (tokens.css L211-220, L546-547). The
       released engine's --shadow-md/-lg are the OLD flat-black hairlines
       (0 4px 12px rgba(0,0,0,.08)), which is below the alpha >= .10 floor, so
       the object roles are declared at the branch's LITERAL recipe here rather
       than aliased onto the local ladder. --brand-deep is a recipe token and
       already exists fleet-wide, so the tint follows this client's brand. */
    --shadow-ink:    var(--brand-deep);
    --shadow-a-hair: color-mix(in srgb, var(--shadow-ink) 10%, transparent);
    --shadow-a-rest: color-mix(in srgb, var(--shadow-ink) 14%, transparent);
    --shadow-a-lift: color-mix(in srgb, var(--shadow-ink) 22%, transparent);
    --shadow-a-deep: color-mix(in srgb, var(--shadow-ink) 28%, transparent);
    --shadow-object:       0 8px 32px var(--shadow-a-rest);
    --shadow-object-hover: 0 18px 44px var(--shadow-a-lift);

    /* TYPE â€” the three role names the engine had no name for (tokens.css
       L450-452), at the engine's values. The contract retunes --fs-lead and
       --fs-label below; that retune is the point, and it needs the role to
       exist first. */
    --fs-lead:  1.25rem;
    --fs-small: 0.875rem;
    --fs-label: 15px;

    /* RHYTHM â€” tokens.css L660-663. Base rung only: the branch's 48/64/80
       breakpoint ladder is deliberately NOT mirrored, because the contract
       pins ONE value from one unlayered line immediately below and mirroring
       the ladder here would fight it. */
    --section-pad-y:    var(--space-xl);                    /* 48 */
    --section-pad-y-sm: calc(var(--section-pad-y) * 0.6);
    --section-pad-y-lg: calc(var(--section-pad-y) * 1.4);

    /* RAIL â€” tokens.css L663 + L1139-1144. The full ladder IS mirrored (16 /
       24 / 32 / 48 / 64), because the seed leaves the gutter at the engine's
       values on purpose and a flat 16px would desync every hand-mirrored
       section. */
    --rail-gutter: var(--space-md);                         /* 16  <480 */
}
@media (min-width: 480px)  { :root { --rail-gutter: var(--space-lg);  } }  /* 24 */
@media (min-width: 768px)  { :root { --rail-gutter: var(--space-32);  } }  /* 32 */
@media (min-width: 1024px) { :root { --rail-gutter: var(--space-xl);  } }  /* 48 */
@media (min-width: 1250px) { :root { --rail-gutter: var(--space-2xl); } }  /* 64 */
/* â•â•â• END ENGINE-ROLE POLYFILL â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */

/* â•â•â• THE CONTRACT, IN TOKENS â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   Where the engine already has the role, the seed READS it and never
   re-declares it. Where the engine has the role at the wrong value for the
   contract, the seed retunes it in ONE line. Only four values are
   client-local, because the engine has no name for them at all. */
:root {

    /* SPACING Â· ONE scale, six values, every one a multiple of 8 and every
       one an engine step. This is the whole scale; a value off it is a
       defect, not a nuance. There is deliberately no client alias for these
       â€” an alias is a second scale waiting to happen.
           8   var(--space-sm)    24  var(--space-lg)    64  var(--space-2xl)
           16  var(--space-md)    40  var(--space-40)    96  var(--space-4xl)
       Below 768 the top two steps step down to 40 and 64, so the mobile six
       are 8 Â· 16 Â· 24 Â· 32 (--space-32) Â· 40 Â· 64. */

    /* RHYTHM Â· 96 top and bottom on every section at 1440, 64 below 768 â€”
       the same number all the way down the page, from ONE declaration.
       tokens.css declares --section-pad-y's ladder inside @layer tokens.base
       and this file is unlayered, so this line wins at every breakpoint with
       no specificity fight. --section-pad-y-sm / -lg follow it automatically.
       A section that needs more presence gets a bigger photograph, not more
       padding. */
    --section-pad-y: var(--space-4xl);                      /* 96 */

    /* FRAME Â· ONE container and ONE rail, both the engine's. --rail-gutter
       (16/24/32/48/64) is left at the engine ladder on purpose: client
       sections are known to hand-mirror it, and moving a rung desyncs every
       mirror silently. The container is --container-max-width. There is no
       --container-max; writing var(--container-max, 1220px) reads an
       undefined property and silently falls back to a literal. */

    /* THE OBJECT Â· one card or band, radius 24, padding 64 top and bottom /
       40 left and right (40 / 24 below 768). Both client-local: the engine has
       --card-padding for the card but no name for the object padding a
       hand-authored section composes on.

       THE VERTICAL AXIS IS THE CORRECTED ONE. Owner, 2026-08-31: "we should
       always have more padding to start around a container like this on top
       and bottom. Like it should be at least 60px top and bottom on desktop."
       64 is the step above 60 on the one scale (clause 1: 8/16/24/40/64/96),
       so the correction lands on the scale instead of opening a seventh value.
       THE INLINE AXIS STAYS 40 on purpose: --pad is also what puts a bare
       lockup and a card's first line on the same left x, and every section
       that mirrors the rail writes `padding-inline: var(--pad)`. Moving the
       inline value would move the rail on the object and not on its mirrors,
       breaking clause 3's tolerance-0 rule at every seam. Two values, both
       members of the ONE scale - not two scales. */
    --pad:   var(--space-40);               /* inline Â· the rail              */
    --pad-y: var(--space-2xl);              /* block  Â· 64, contract clause 4 */

    /* RADIUS Â· two radii carry the page. Both are engine roles:
           surfaces  var(--radius-object)   24  (card, band, panel, frame)
           controls  var(--radius-pill)    999  (every control)
       Role values, not extra choices: inputs var(--radius-control), <img> 0
       (the frame rounds it). A third surface radius is a bug. */

    /* TYPE Â· 5â€“6 rungs page-wide, at most 4 in any one section. Three are
       engine roles retuned here; three are client-local because the engine
       has no name for them.
           display   var(--fs-display)     ONE per page, below the hero
           h1        var(--fs-h1)
           title     --fs-title            THE SAME VALUE IN EVERY SECTION
           card      --fs-card
           body      var(--fs-body) at 1.6
           label     var(--fs-label)       uppercase, wide
       --fs-stat is the one display-scale numeral a stat band spends. Retune
       the clamps to this brand; keep the count. */
    /* RETUNED 2026-09-09 at §3.1a to the signed docs/brand-dna.md Type Ladder.
       The seed's defaults were all contract-legal; these are the same rungs
       pinned to THIS site's declared values, so DESIGN.md §3.1 and the CSS
       cannot drift. Was: title max 42, card 1.375rem, label 12px, tracking
       0.14em. */
    --fs-title: clamp(30px, 3vw, 40px);     /* sheet: section title 40         */
    --fs-card:  1.5rem;                     /* sheet: card title 24            */
    --fs-stat:  clamp(32px, 3vw, 44px);     /* the one display moment          */
    --fs-lead:  1.0625rem;                  /* sheet: body/lead 17             */
    --fs-label: 11px;                       /* sheet: eyebrow 11               */
    --eyebrow-tracking: 0.18em;             /* sheet: eyebrow +0.18em          */

    /* COLOUR Â· 60/30/10, one hue family, accent on primary actions only.
       Every value is a recipe token, so this seed carries no hex: the brand
       is published from the design widget and this file must never fork it.
           60  var(--bg-page) / var(--surface)      neutral grounds
           30  var(--bg-dark-section) / var(--brand)  ink and dark bands
           10  var(--accent)                         primary actions only
       The page needs >= 3 grounds and >= 2 dark-or-deep, one below the hero. */

    /* DEPTH Â· ONE recipe, two states, ink-tinted, alpha >= .10, on elevated
       objects only â€” never a band, a photograph or the page ground. Never a
       border AND a shadow on the same object. Grids over 12 cards ship none.
           rest   var(--shadow-object)        (--shadow-md, alpha .14)
           hover  var(--shadow-object-hover)  (--shadow-lg, alpha .22) */

    /* MOTION Â· ONE hover vocabulary page-wide: translateY(-3px) + shadow
       bloom + inner image scale(1.04), on click targets only, with a
       :focus-visible reaching the same element. The contract says 180ms; the
       engine's duration scale steps 120 / 200 / 300 / 450 and 200 is the
       rung, so the page spends --dur-2 rather than opening a seventh
       duration nobody else reads. */
    --lift: -3px;
}

@media (max-width: 767px) {
    :root {
        --section-pad-y: var(--space-2xl);  /* 64 below 768 */
        /* THE OBJECT below 768. The block axis steps to 40 rather than staying
           at 24: the correction Kyle asked for is vertical, and 40 is the
           mobile scale's step under 64 (8/16/24/32/40/64), so the phone keeps
           the same generosity at one rung down. The inline axis stays 24 â€”
           at 393 the object is nearly full-width and every extra inline pixel
           comes straight out of the measure. */
        --pad:   var(--space-lg);           /* 24 below 768 Â· inline */
        --pad-y: var(--space-40);           /* 40 below 768 Â· block  */
    }
}

/* â•â•â• THE BINDING â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   The contract's one test is whether a stranger can name the single object
   the page is made of. That fails the moment a hand-authored `.sjm-card` and
   a shipped `.hzo-card` are two different objects on one page.

   So the engine's card is pointed at the same recipe, from its own tokens,
   in four lines. This is variable shadowing â€” the sanctioned override route
   â€” not a redefinition of the component: the engine's own declarations read
   these names and simply resolve differently here.

   Any component that exposes its radius / padding / shadow as tokens joins
   the object the same way. One that does not is an engine gap: raise it,
   do not fork the component's selector. */
:root {
    --card-radius:  var(--radius-object);
    /* A `padding:` shorthand pair, block then inline â€” the same two values
       .sjm-card spends. No component reads --card-padding at engine v1.11.5,
       so this is a forward binding: the day one does, it inherits the object's
       corrected vertical padding rather than a stale single value. */
    --card-padding: var(--pad-y) var(--pad);
    --card-shadow:  var(--shadow-object);
    --card-border:  transparent;   /* never a border and a shadow together */
    --frame-radius: var(--radius-object);

    /* THE CONTROL BOUNDARY. components/form/form.css sets every field's
       border with `!important`, reading `var(--input-border, var(--border))`.
       So a Layer-3 `border:` on an input is a DEAD declaration â€” the only
       thing that reaches the form is this token. The engine's fallback
       --border is #E5E7EB, which on a white field is 1.24:1, against WCAG
       1.4.11's 3:1 for the boundary of a user-interface component: measured
       on 6.hoziodev.com/home-r2/ 2026-08-28, where the page's own
       `border: 1px solid rgba(22,33,31,.52)` (3.42:1) never applied.
       Ink-tinted from the brand rather than a grey, at the alpha that clears
       3:1 on the field ground. RETUNE PER BRAND and re-measure: the value
       must clear 3:1 against --input-bg, and a light --brand-deep will not. */
    --input-border: color-mix(in srgb, var(--shadow-ink) 52%, transparent);
}

/* â•â•â• THE SECTION ROOT â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   EVERY section root on EVERY page of this site carries BOTH classes:

       <section class="hzo-section cl-sec cl-<name> â€¦">

   `hzo-section` is the ENGINE's section â€” `page_spec` refuses a raw section
   root without it (PS-RAW-NO-SKELETON) â€” and `cl-sec` is this site's. Two
   readers, one decision, stated once in each place they look. The three
   paragraphs below are what that pairing costs and what it buys.

   1 Â· THE SEAM. WordPress emits

           :root :where(.is-layout-flow) > * { margin-block-start: 24px }   (0,1,0)

   over the children of a flow-layout post body, so without a counter-rule
   every section wears a 24px stripe of the PAGE ground above it â€” a white
   seam between every pair of sections on any page whose sections are
   coloured. The engine ships a counter-rule but keys it on the engine class
   alone (`:is(.is-layout-flow,.is-layout-constrained) > .hzo-section`), so a
   Layer-3 root carrying only the client class is never reached by it.
   MEASURED 2026-08-28 on 6.hoziodev.com/home-r2/: five 24px #FFFFFF seams
   down an ivory-and-ink page â€” one of them directly under the hero
   photograph, where it reads as a rendering fault rather than a design
   choice â€” with nothing wrong in the page's own stylesheet and every gate
   GREEN. The reset below names the client class too and lands at (0,2,0), so
   it BEATS the platform rule outright instead of tying it and depending on
   sheet order.

   2 Â· THE RHYTHM. `.sjm-sec` is where --section-pad-y is finally SPENT. At
   engine v1.11.5 nothing reads that token (base.css still hardcodes the
   .hzo-section 48/64/80 ladder), so the contract's one-line retune above is
   inert until a selector consumes it. Padding, not margin: on a continuous
   coloured field a section's own air belongs inside its own box, and adjacent
   margins collapse into one.

   3 Â· WHY THE RHYTHM IS WRITTEN AS A COMPOUND. Carrying `hzo-section` also
   hands the root to a second engine rule â€”

           .hzo-section + .hzo-section { padding-top: 0 }                   (0,2,0)

   (components/_base/doc-layout-toc.css) â€” which collapses the doubled padding
   of a white-on-white run and restores it only for the grounds it can SEE:
   `--pale`, `--tint`, `--dark`, `.hzo-hero`. A section whose ground is a
   CLIENT class is invisible to those exceptions. Left alone on the six-section
   page above, that rule computes top padding 80 Â· 0 Â· 0 Â· 80 Â· 80 for sections
   2â€“6: two sections with their content jammed against the colour edge over
   them, three more at the engine's 80 instead of the contract's 96.
   `.hzo-section.sjm-sec` is also (0,2,0) and this file is enqueued LAST, so it
   ties on specificity and wins on order, and every section is back to 96/64
   from the page's one number. The bare `.sjm-sec` selector stays for a root
   that carries no `hzo-section`; the compound is what makes the pairing safe.
   Do not "tidy" the two selectors into one â€” they are two specificities on
   purpose. */
.sjm-sec,
.hzo-section.sjm-sec { padding-block: var(--section-pad-y); }

:is(main, .entry-content, .is-layout-flow, .is-layout-constrained)
    > :is(.sjm-sec, .hzo-section, [class*="hzo-section--"]) {
    margin-block-start: 0;
}

/* â•â•â• THE OBJECT â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   The vocabulary a hand-authored AMPLIFY section composes on, so a RAW
   section and a shipped component are the same object. Every rule carries
   the contract clause it came from. */

/* THE OBJECT Â· a card on the page ground. Radius 24, padding 64/40 (40/24
   below 768), one ground from the palette. Do not invent a second object. */
.sjm-card {
    background: var(--surface);
    border-radius: var(--radius-object);
    padding: var(--pad-y) var(--pad);
}
/* DEPTH Â· elevated objects only. A flat card in a grid of 12+ ships none. */
.sjm-card--lift { box-shadow: var(--shadow-object); }

/* THE OBJECT Â· the same object as a band. A band is a ground change, so it
   takes no shadow â€” depth belongs to things that are lifted off the page. */
.sjm-band {
    background: var(--bg-alt);
    border-radius: var(--radius-object);
    padding: var(--pad-y) var(--pad);
}
.sjm-band--ink   { background: var(--bg-dark-section); color: var(--text-on-brand); }
.sjm-band--brand { background: var(--brand);           color: var(--text-on-brand); }

/* PHOTOGRAPHY LEADS Â· photos fill their frame, no filter, no tint. The frame
   rounds the image, so the image itself is square (radius 0 is its role
   value). Ratio composes from .hzo-aspect--*; this owns only the clipping. */
.sjm-frame {
    display: block;
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-object);
    background: var(--bg-alt);
    margin: 0;
}
.sjm-frame > img,
.sjm-frame > video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 0;
}

/* TYPE Â· the section opening, as ONE lockup on the rail. Every section opens
   with eyebrow â†’ title â†’ lead â†’ actions, in that order, on the same left x.
   Four rungs, which is the per-section ceiling. */
.sjm-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
    font-family: var(--font-body);
    font-size: var(--fs-label);
    font-weight: var(--fw-600);
    line-height: var(--lh-none);
    letter-spacing: var(--eyebrow-tracking);
    text-transform: uppercase;
    /* COLOUR Â· --brand-text, NOT --accent-text. The two role names read as
       interchangeable and are not: --accent-text is the accent hue darkened
       just far enough to be legible, and at 12px/600 on a warm neutral it
       lands at 4.00:1 â€” under AA's 4.5 for text that is not "large". Measured
       2026-08-28 on 6.hoziodev.com/home-r2/: #208760 on ivory #F5F2EC = 4.00:1
       and on a white card = 4.47:1, i.e. EVERY eyebrow on a light ground
       failed, in all six sections, with the page's contrast audit passing
       because it had checked --brand-text. --brand-text is the role for
       "the brand colour used AS TEXT" and measures 9.19:1 / 10.27:1 on the
       same two grounds. The accent stays where clause 10 puts it: on the
       primary action. On a dark band the inversion below takes over. */
    color: var(--brand-text);
    margin: 0;
    /* CLAUSE 8 Â· THE EYEBROW SITS 24px ABOVE ITS TITLE. ONE fixed number, box
       to box, the same in every section and at every rung. The owner set it by
       eye on a slider on 2026-08-31 at 25px; it ships as 24, the step on
       clause 1's scale, because 1px is invisible and the ONE-spacing-scale
       rule depends on staying on it.

       v1.1.2's font-metric trim (`--cap-trim`) is retired. The parsed metrics
       did not match what the browser rendered: one 8px computation drew ~4px
       in the hero and ~35px in a section title, and the value went stale
       whenever a rule retuned a heading's leading without restating the trim
       beside it. A fixed number does neither.

       THE ARITHMETIC. The lockup is a flex column with `gap`, so this margin
       ADDS to that gap. The eyebrow therefore spends the DIFFERENCE, nothing
       else â€” two terms, one line:
           .sjm-lockup       gap 16  ->  24 - 16 = margin  8  ->  16 + 8 = 24
           a hero lockup    gap 24  ->  24 - 24 = margin  0  ->  24 + 0 = 24
           no gap declared  fallback 0  ->      margin 24    ->   0 + 24 = 24

       THE CONTRACT, unchanged: a stack holding an eyebrow declares
       --lockup-gap and reads it back into `gap`. Retune THAT, never `gap`
       directly. Because this line names the same variable the gap reads, both
       halves move together and the pair cannot go stale. */
    margin-bottom: calc(var(--space-lg) - var(--lockup-gap, 0px));
}
/* TYPE Â· the section title. ONE value in every section â€” repeating a rung is
   the norm, not a failure of imagination. */
.sjm-title {
    font-family: var(--font-heading);
    font-weight: var(--fw-700);
    font-size: var(--fs-title);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-tight);
    text-wrap: balance;
    max-width: 20ch;
    margin: 0;
    color: inherit;
}
/* TYPE Â· body at 1.6, measure 50â€“80ch. */
.sjm-lead {
    font-size: var(--fs-lead);
    line-height: var(--lh-normal);
    max-width: 56ch;
    margin: 0;
    color: var(--text-body);
}
/* AIR Â· minimum 12px between two unrelated elements. Under 12px is legal
   only INSIDE one lockup, which is what this is. */
.sjm-lockup {
    /* The stack's general gap, named so .sjm-eyebrow can offset against it.
       A section retunes THIS, never `gap`. */
    --lockup-gap: var(--space-md);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--lockup-gap);
}
/* CLAUSE 19 Â· ONE ALIGNMENT PER SECTION. Every element in a section shares
   one text alignment, and this page's is left, on the one rail (clause 3).
   Owner, 2026-08-31: "dont align some text to left and center some. They
   should be uniform." A section that centres, centres its eyebrow too â€” so a
   centring section flips BOTH axes on the lockup and never `text-align`
   alone, which is the mixed state the correction names: `text-align: center`
   with `align-items: flex-start` renders a left-hung eyebrow and a left-hung
   button row over centred body copy, and every one of those declarations
   looks correct on its own line. */
.sjm-lockup--center {
    align-items: center;
    text-align: center;
}
/* On a dark ground the lockup inverts from the band, not from a second set
   of classes. */
.sjm-band--ink .sjm-eyebrow,
.sjm-band--brand .sjm-eyebrow { color: var(--accent); }
.sjm-band--ink .sjm-lead,
.sjm-band--brand .sjm-lead { color: var(--text-on-brand); }

/* RADIUS Â· 999 on every control. THE ONE CONTROL, three treatments that stay
   tellable apart with the colour removed: filled / outlined / bare. */
.sjm-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 48px;                       /* >= 44 touch target */
    padding: 0 var(--space-lg);
    border-radius: var(--radius-pill);
    font-family: var(--font-body);
    font-size: var(--fs-body);
    font-weight: var(--fw-600);
    line-height: var(--lh-none);
    white-space: nowrap;
    border: var(--border-2) solid transparent;
    transition: transform var(--dur-2) var(--ease-out),
                background-color var(--dur-2) var(--ease-out),
                border-color var(--dur-2) var(--ease-out),
                box-shadow var(--dur-2) var(--ease-out);
}
/* COLOUR Â· the accent appears on the primary action and nowhere else.

   âš  THE DOUBLED CLASS IS THE POINT â€” DO NOT "TIDY" IT TO ONE.
   Each variant is written as a COMPOUND on the control itself,
   `.sjm-pill.sjm-pill--primary`, which is (0,2,0). Written as the single class
   it looks like, it is (0,1,0), and the engine's own context rule
   `.hzo-section--dark a { color: #fff }` (components/_base/dark-section.css)
   is (0,1,1) and REPAINTS IT â€” silently, with the page's own stylesheet
   saying exactly the right thing and every gate GREEN.
   MEASURED 2026-08-28 on 6.hoziodev.com/home-r2/: the hero's and the CTA
   band's primary pill rendered #FFFFFF on the mint accent at 1.70:1 against
   the intended 8.65:1 â€” a WCAG-AA failure on the two loudest buttons on the
   page, invisible in the source CSS and invisible to every static gate.
   The compound is on the CONTROL and not on the ancestor
   (`.hzo-section--dark .sjm-pill--primary` would also win here): a rule that
   does not have to name the ancestor repainting it also survives `.on-photo`,
   a dark card, a dark widget surface, and the next engine context class
   nobody has written yet. Enumerating ancestors fixes one site once; this
   fixes the class of bug. Same reason on --outline and --ghost. */
.sjm-pill.sjm-pill--primary { background: var(--accent); color: var(--text-on-accent); }
.sjm-pill.sjm-pill--outline { background: transparent;   color: var(--brand-text); border-color: var(--brand); }
/* On a photograph or a dark band. No shadow at any state: the contract
   forbids a border and a shadow on one object, so its bloom is a ground
   bloom. */
.sjm-pill.sjm-pill--ghost   { background: transparent;   color: var(--text-on-brand); border-color: var(--text-on-brand); }

/* â•â•â• THE ONE HOVER VOCABULARY â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   translateY(-3px) + shadow bloom + inner image scale(1.04), on click
   targets only. Declared once, here, for the whole page. Anything that
   hovers differently is a second vocabulary and a defect. */
@media (hover: hover) {
    a.sjm-card:hover,
    .sjm-card-link:hover .sjm-card {
        transform: translateY(var(--lift));
        box-shadow: var(--shadow-object-hover);
    }
    .sjm-pill--primary:hover { transform: translateY(var(--lift)); box-shadow: var(--shadow-object-hover); }
    .sjm-pill--outline:hover { transform: translateY(var(--lift)); background: var(--brand); color: var(--text-on-brand); }
    .sjm-pill--ghost:hover   { transform: translateY(var(--lift)); background: color-mix(in srgb, var(--text-on-brand) 18%, transparent); }
    a:hover > .sjm-frame--zoom > img,
    a:hover .sjm-frame--zoom > img,
    .sjm-card-link:hover .sjm-frame--zoom > img { transform: scale(1.04); }
}
.sjm-frame--zoom > img { transition: transform var(--dur-4) var(--ease-out); }

/* FOCUS PARITY Â· every :hover subject above has a :focus-visible reaching the
   same element. 3px outline at 3px offset â€” the contract's numbers, which are
   one step above the engine's 2/2 default. This is not optional polish; a
   hover with no focus partner is a keyboard user with no affordance. */
:focus-visible { outline: 3px solid var(--color-focus); outline-offset: 3px; }
.sjm-band--ink :focus-visible,
.sjm-band--brand :focus-visible { outline-color: var(--accent); }
a:focus-visible > .sjm-frame--zoom > img,
a:focus-visible .sjm-frame--zoom > img { transform: scale(1.04); }

/* The engine's a11y net zeroes transition DURATION under reduced motion; it
   cannot zero a transform, which would then apply instantly. Name the
   subjects that move. */
@media (prefers-reduced-motion: reduce) {
    a.sjm-card:hover,
    .sjm-card-link:hover .sjm-card,
    .sjm-pill:hover { transform: none; }
    .sjm-frame--zoom > img { transition: none; }
    a:hover .sjm-frame--zoom > img,
    a:focus-visible .sjm-frame--zoom > img,
    .sjm-card-link:hover .sjm-frame--zoom > img { transform: none; }
}

/* â•â•â• THE LINK POLICY (clause 17) â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
   An underline means "this word, in this sentence, is a link". It does NOT
   mean "this is clickable": a pill, a nav item, a card and a phone control
   each say that with their own shape, and underlining them says it twice.
   Owner, 2026-08-31: "we shouldn't underline all the links, only underline if
   it is a hyperlink in body text."

   WHY THIS BLOCK HAS TO EXIST. The engine's base.css ships
   `a { text-decoration: var(--link-decoration, underline) }`, so every anchor
   on a Hozio page arrives underlined and each engine component has to cancel
   it for itself. Nothing cancels it for a class this site invents, which is
   how an underlined pill label shipped â€” and nothing cancels it for the
   chrome, fact rails and tel links a page adds later either.

   SO THE DEFAULT IS INVERTED, NOT PATCHED. Enumerating every control that
   must not be underlined is a list that is wrong the moment a page adds a
   class to it; the measured proof is a live page where six control classes
   were named and four more (a utility bar, a hero fact rail, a tel control)
   were still underlined because nobody had thought to name them. So the
   engine's own hook is re-pointed once, and body copy opts back in. Reading
   an engine token this way is the sanctioned override route
   (styling-layers.md, "scoped custom-property shadowing"), and it is one
   declaration instead of a list that has to be maintained.

   REST STATE, NOT HOVER. Hover keeps the page's ONE vocabulary (lift + bloom
   + inner image scale). An underline is never the hover treatment â€” adding
   one on hover is a second vocabulary and a defect. */

/* 0 Â· THE DEFAULT. The engine reads this token for every `a`; unset it here
      and controls, nav, chrome, card links, fact rails and tel links are all
      clean at rest without being named. */
:root { --link-decoration: none; }

/* 1 Â· BODY COPY OPTS BACK IN â€” the one place an underline belongs: an inline
      link inside a paragraph, a lead, prose list text, or a form's consent
      sentence (Fluent renders that one inside `label > span`, not a `<p>`,
      which is why it is named).
      `li a` is deliberately NOT blanket, and `.entry-content` is deliberately
      NOT one of its ancestors: that wrapper is the whole page, so
      `.entry-content li a` reaches every list on it. Measured 2026-08-31 on
      6.hoziodev.com/home-r2/ â€” it underlined the hero's trust rail, which is a
      link list, not prose. A list item is as often a link list (a footer
      column, a fact rail, a card grid) as it is prose. `.hzo-prose` is the one
      region that is prose by declaration, so it is the only one here.
      The ancestors sit in `:where()` so each selector stays at (0,0,2) and a
      control class (0,1,0) beats it without needing an exception. */
:where(main, .hzo-section, .hzo-prose, .entry-content, .sjm-card) p a,
:where(.hzo-prose) li a,
:where(.hzo-form) label a,
.sjm-lead a {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
}

/* 2 Â· CONTROLS AND LINK LISTS â€” never underlined, including inside body copy.
      Rule 0 already gets them there; this list is what holds when a control
      sits inside a paragraph or a lead. `.sjm-cue` is the name for a card's or
      a section's text cue ("Read the kitchen page"): a card link, not body
      copy. quality-rules Â§11.5 still wants a rest signal on a card link and
      allows three â€” chevron, underlined title, visible edge â€” so a card that
      drops the underline spends one of the other two.
      `.sjm-lead a` is (0,1,1) and outranks a bare `.sjm-pill`, so the pill
      inside a lead is written compound. */
.sjm-pill,
.sjm-eyebrow,
.sjm-cue,
a.sjm-card,
.sjm-card-link,
.sjm-tel,
.sjm-link-list a,
.sjm-lead a.sjm-pill {
    text-decoration: none;
}

/* â•â•â• SITE-WIDE â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â• */

/* Bare-prose safety net â€” ships with the flow-layout <main> (PF-064).
   templates/index.html uses layout:"default" so every .hzo-section can paint
   its background across the full viewport. The trade-off is that post_content
   is no longer constrained by the template, and post_content is editable in
   wp-admin: a paragraph pasted outside the section skeleton would run the
   full viewport width, which at 1920px is unreadable.

   So bare text-level blocks that are NOT inside a section get the container
   treatment back. Deliberately narrow â€” it names text-level blocks only and
   never touches section, .hzo-section, or anything a page intentionally
   full-bleeds. :where() keeps specificity at 0 so any real rule still wins.
   Keep this when adding site-wide rules below. */
:where(.entry-content) > :is(p, h1, h2, h3, h4, h5, h6, ul, ol, dl, blockquote, figure, table, pre) {
    max-width: var(--container-max-width, 1350px);
    margin-inline: auto;
    padding-inline: var(--space-md);
}

/* â”€â”€â”€ Home â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */

/* â”€â”€â”€ Services â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */

/* â”€â”€â”€ About â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */

/* â”€â”€â”€ Contact â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */

/* ═══ PALETTE CORRECTION — the signed Deep, restored ══════════════════════
   docs/brand-dna.md locks Deep at #040810, measured at 1.550:1 against
   Primary — the floor is 1.500 and seven lighter candidates were computed and
   rejected before it was chosen.

   --brand-deep is NOT a recipe input. The engine derives it from --brand
   through the OKLCH pass, and on 2026-09-09 that derivation published
   #060f24, which measures 1.474:1 against Primary. That is BELOW the floor.
   Left alone, every dark ground in DESIGN.md 4.1 and every shadow tint in
   4.3 would be built on an anchor the signed sheet already rejected, and
   nothing would have reported it.

   Setting an engine variable on :root from the client layer is the sanctioned
   override route (styling-layers.md, "scoped custom-property shadowing"), and
   it is the correction DESIGN.md 2 specified in advance as the right fix
   rather than a fourth hex in the sheet. custom.css loads last, so this wins
   over tokens.overrides.css.

   ONLY --brand-deep is corrected. The rest of the derived family
   (--brand-text, --brand-pale, the hover pair) is the engine's and stays the
   engine's. Delete this block the day the derivation lands >= 1.500 on its own.
   ═══════════════════════════════════════════════════════════════════════ */
:root {
    --brand-deep: #040810;
}

/* SQUARE CONTROLS — operator, 2026-09-10: "all buttons should have 0px border
   radius". This overrides design-contract clause 5, which puts 999 on every
   control; it is the operator's call and it is recorded rather than silently
   applied. --btn-radius is the engine's own hook (buttons.css:37 reads
   var(--btn-radius, var(--radius-pill))), so one declaration squares every
   .hzo-btn on the site instead of a rule per button. --radius-pill itself is
   left alone on purpose: the eyebrow dot and other genuine circles read it. */
:root {
    --btn-radius: 0;
}

/* ═══ COMPONENT TREATMENT — the four setup families ═══════════════════════
   Authored at §3.1f from docs/component-treatment.md, against the signed
   docs/brand-dna.md (motif: layered-planes · register: documentary ·
   temperament: calm). Every block sits behind its @treatment marker so gate
   T3 can see it. Presentation only — no markup contracts, no ARIA, no focus
   order. Values come from the seed above; nothing here re-declares a token.
   ═══════════════════════════════════════════════════════════════════════ */

/* @treatment nav-header */
/* THE BAR IS THE PLANE, ALWAYS — operator, 2026-09-10. Overrides the signed
   form (transparent over the hero, plane arriving on scroll): the change WAS
   the problem, and the transparent state left the nav unreadable over light
   sections. Only the shadow still reacts to scroll, and that is not a colour. */
.sjm-header {
    position: sticky;
    top: 0;
    z-index: var(--z-header);
    background: var(--brand-deep);
    color: var(--text-on-brand);
    transition: box-shadow .18s var(--ease-out);
    /* The bar's air lives HERE, not on the inner container - that is what lets
       the stretched nav's bottom edge BE the panel's top edge, with no dead
       strip between them to lose hover in. */
    padding-block: var(--space-sm);
}
.sjm-header__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-lg);
    /* Zeroed deliberately: the bar's air is owned by .sjm-header above, so
       this container's padding box equals its content box - which is what the
       panel's top edge is measured from. */
    padding-block: 0;
}
/* [hzo_nav]'s engine markup, skinned in place: a page-scoped descendant
   override is the sanctioned route when the element carries no hook of ours
   (styling-layers.md). Presentation only — never behaviour or a11y. */
.sjm-header .hzo-site-nav__link {
    font-family: var(--font-body);
    font-size: var(--fs-label);
    font-weight: 600;
    letter-spacing: var(--eyebrow-tracking);
    text-transform: uppercase;
    text-decoration: none;
    transition: color .15s var(--ease-out);
}
/* Hover brightens the ink; it does NOT go accent. brand-dna.md restricts the
   accent to primary CTAs and ACTIVE states, and _chrome.md 6 bans it on chrome
   links outright. The current page IS a genuine active state, so it keeps it. */
/* The engine paints :hover with !important at (0,3,0), so this needs both the
   weight and the matching specificity or it loses — which it did, and on the
   plane that rendered the hovered item in --brand at 1.55:1. */
.sjm-header .hzo-site-nav__link:hover { color: var(--sjm-nav-hover) !important; }
.sjm-header .hzo-site-nav__link:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
}
/* DEFECT FIX. The engine paints .hzo-site-nav__link with !important, so this
   rule never applied - the active link has been engine navy, not the specified
   rust. Matching the weight lands it. */
/* One set of nav inks, not three states of them. Rest sits under full white so
   hover has somewhere to go (~12:1 here). Active is white, not rust: rust on
   this ground is 3.03:1 - fine for a mark, under the 4.5 a label needs - so the
   rust moves to the underline bar below. */
.sjm-header {
    --sjm-nav-ink: rgba(255, 255, 255, .78);
    --sjm-nav-hover: var(--text-on-brand);
    --sjm-nav-active: var(--text-on-brand);
    /* The engine paints the OPEN item (.hzo-nav-item:hover / .is-open >
       link, desktop-nav.css:58-60) in --nav-link-hover, which defaults to
       --brand: ~1.5:1 on this bar, so "Services" vanished while its panel was
       open. Retuning the engine's own token fixes all three paths at once. */
    --nav-link-hover: var(--text-on-brand);
}
.sjm-header .hzo-site-nav__link { color: var(--sjm-nav-ink) !important; }
.sjm-header .hzo-site-nav__link.is-active { color: var(--sjm-nav-active) !important; }
/* The engine draws the active bar in currentColor. On the plane the label goes
   light, so the bar is pinned to rust to keep the accent carrying the signal. */
.sjm-header .hzo-site-nav__link.is-active::after { background: var(--accent); }
.sjm-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-lg);
}
/* Secondary tier: stroke, no fill, so it is tellable from the primary with the
   colour removed. Hidden below 1024; the mobile menu carries the number. */
/* Only the shadow reacts to scroll now. The ground is constant (see the base
   rule above), so there is no plane to arrive and no :has() state to maintain. */
.sjm-header.is-stuck { box-shadow: var(--shadow-object); }
/* The logo is a navy monogram over a dark wordmark, invisible on this ground.
   A filter, never a transform — header-condense.css owns transform here. This
   flattens the mark to white and loses the rust in it; a proper light-variant
   logo asset is the real fix and is worth asking the client for. */
.sjm-header .hzo-site-logo img {
    filter: brightness(0) invert(1);
    /* Operator, 2026-09-10: bigger logo, tighter bar. The height is raw because
       the engine gives the logo no size token. */
    height: 76px;
    width: auto;
}

/* ── THE MEGA PANEL ─────────────────────────────────────────────────────────
   STRUCTURE IS NOT AUTHORED HERE. A 2nd-level menu item that has children of
   its own becomes a column group, and that alone forces the wide panel
   (structural-shortcodes.php:103-124). Editing the menu tree changes the panel;
   editing this block changes only its FORM.

   Clause 5: the panel is a surface, so --radius-object. Its top corners are 0
   because it is flush to the bar — the same role value images take, so the page
   still carries only two radii.
   Clause 11: shadow, no border. The engine ships BOTH a 2px accent top rule and
   a drop shadow, and "never border and shadow on one object" makes keeping the
   pair a defect. The rule goes; the ground change is the one separator idiom
   (review row 8), and the rust is spent on state instead of on an edge. */
.sjm-header .hzo-mega-menu {
    background: var(--brand-deep);
    border-top: 0;
    border-radius: 0 0 var(--radius-object) var(--radius-object);
    box-shadow: var(--shadow-object);
    /* Chrome, not a page object: clause 4's 64/40 governs sections, and this
       pair is still off clause 1's one scale. */
    padding: var(--space-xl) var(--space-lg);
    gap: var(--space-lg);
    /* The engine clips to keep link-hover fills off the rounded corners; the
       chips sit a full --space-xl inside so there is nothing to clip, and
       visible overflow is what lets the 3px/3px focus ring show (clause 17). */
    overflow: visible;
}
/* PANEL GEOMETRY. The engine centres a wide panel on .hzo-site-nav, whose
   centre is not the page's: at 1250px its left edge sat at -29px, clipping
   column 1 off the viewport. Anchoring to the header's container element puts
   column 1 on the logo to the pixel at 1250/1440/1920; arithmetic was tried
   and rejected, since the homepage widens the chrome container to 1390 from
   its own page meta. The bleed is painted, not positioned — a spread shadow
   carries the ground to the viewport, the clip keeps it horizontal. That also
   settles clause 11: a band is not an elevated object, so its only shadow is
   the bleed, and radius goes to 0 as the cta band already does. */
.sjm-header .hzo-site-nav { position: static; }
.sjm-header__inner { position: relative; }
.sjm-header .hzo-mega-menu--wide {
    left: 0;
    right: 0;
    width: auto;
    max-width: none;
    border-radius: 0;
    padding-inline: var(--rail-gutter);
    box-shadow: 0 0 0 100vmax var(--brand-deep);
    clip-path: inset(0 -100vmax);
    /* THE GAP THAT CLOSED THE PANEL. The panel hangs off the container's
       padding box, so the nav row must END on that same edge or the strip
       between is dead space: the pointer crosses it, hover is lost and the
       panel shuts before the cursor arrives. The bar's air lives on
       .sjm-header, not here, and the nav stretches to fill. Measured: gap 0. */
    top: 100%;
}
/* And the other half of it: the nav must actually reach that content-box edge,
   or the items still stop short of the panel. */
.sjm-header .hzo-site-nav {
    align-self: stretch;
    align-items: stretch;
    /* The engine gives the nav an 80px min-height (--header-height-desktop), so
       the nav, not the logo, was setting the bar's height — which is why the
       bar stayed tall no matter how much padding came off it. */
    min-height: 0;
}
/* HOVER INTENT, the CSS half. The grace-period driver in custom.js owns open
   and close via .is-open, so the engine's pure :hover open must stand down or
   the two disagree the moment the pointer crosses a sibling. :not(.is-open)
   limits this to the hover-only case, leaving the keyboard path alone; if the
   script never runs, the class never lands and plain :hover still governs. */
.sjm-header .sjm-nav--hover-intent .hzo-nav-item:hover:not(.is-open) > .hzo-mega-menu {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-8px);
}

/* The X half of the engine's transform was only ever the centring trick. The Y
   half is the entrance, so it is re-stated rather than dropped — killing the
   transform outright would take the slide with it. */
.sjm-header .hzo-mega-menu--wide { transform: translateY(-8px); }
.sjm-header .hzo-nav-item:hover > .hzo-mega-menu--wide,
.sjm-header .hzo-nav-item.is-open > .hzo-mega-menu--wide { transform: translateY(0); }

/* Group titles take the site's label rung so the panel reads as the bar's own
   material. The engine's --text-muted is 4.15:1 here; this is 10.3:1. */
.sjm-header .hzo-mega-menu__group-title {
    display: block;
    margin-bottom: var(--space-md);
    font-family: var(--font-body);
    font-size: var(--fs-label);
    font-weight: 600;
    line-height: 1.35;
    letter-spacing: var(--eyebrow-tracking);
    text-transform: uppercase;
    text-decoration: none;
    color: rgba(255, 255, 255, .72) !important;
    /* Two lines' worth, always: a wrapping title was pushing its column 17px
       below the other four. Measured, the wrapped box is 39.7px, so 40 - the
       step on clause 1's scale, not a number picked to fit. */
    min-height: 40px;
}
.sjm-header a.hzo-mega-menu__group-title:hover { color: var(--text-on-brand) !important; }
/* Sentence case at 15px under the group title's 11px caps is the same
   eyebrow-over-content relationship every section uses. Raw size: engine nav
   sizes are not on the token ladder (mega-menu sheet, axis F). */
.sjm-header .hzo-mega-menu__link {
    padding: 10px var(--space-md);
    border-radius: var(--radius-pill);
    font-size: 0.9375rem;
    font-weight: 500;
    line-height: 1.35;
    text-decoration: none;
    color: rgba(255, 255, 255, .86) !important;
    transition: background-color .18s var(--ease-out), color .18s var(--ease-out);
}
.sjm-header .hzo-mega-menu__link:hover {
    color: var(--text-on-brand) !important;
    background: rgba(255, 255, 255, .08);
}
.sjm-header .hzo-mega-menu__link:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
}
/* Current page. The engine paints this --brand-text, which is 1.3:1 on the
   plane. Filling the pill instead puts white on rust at 6.6:1 and keeps the
   accent doing what brand-dna.md allows it to do — primary actions and active
   states — without introducing a second object or a second shadow. */
.sjm-header .hzo-mega-menu__link.is-active {
    color: var(--text-on-brand) !important;
    font-weight: 600;
    background: var(--accent);
}
/* Hub and sub-page. The tree is four levels and the engine renders three, so a
   hub and its sub-pages share a column; custom.js marks the subs from their
   URLs. Indented, a step smaller and a step dimmer (~8.6:1) under their hub. */
.sjm-header .hzo-mega-menu__link.sjm-nav-sub {
    padding-inline-start: calc(var(--space-md) * 2);
    font-size: .875rem;
}
.sjm-header .hzo-mega-menu__link.sjm-nav-sub:not(:hover):not(.is-active) {
    color: rgba(255, 255, 255, .66) !important;
}
/* A hub that follows another hub's subs opens a new family, so it gets air. */
.sjm-header .hzo-mega-menu__list > li:has(> .sjm-nav-sub) + li:not(:has(> .sjm-nav-sub)) {
    margin-top: var(--space-md);
}
.hzo-mobile-menu .hzo-mobile-subaccordion .hzo-mobile-panel > .hzo-mobile-menu__link.sjm-nav-sub {
    padding-left: 64px;
}
/* @end-treatment */

/* @treatment cards */
/* CORRECTED at §4.1 after walking component-sheets/card.md. This block first
   targeted .sjm-card__media / __body / __title — pure client classes, i.e. RAW
   markup, which quietly reimplemented four things the engine already does
   correctly: the stretched-link a11y contract, equal-height bodies, media
   clipping, and a reduced-motion-safe hover zoom.

   The declared path in docs/component-treatment.md is class-hook+L3, and this
   is now that: `class="sjm-card"` rides the engine card root, and the hooks
   below scope the engine's own BEM parts underneath it. Never redefine
   .hzo-card__title globally — it is shared with steps, stat-block and every
   card on the site, and its 1.25rem is a fixed value the sheet says to leave
   alone. The 24px card rung arrives through this client scope instead. */
.sjm-card {
    --card-radius: var(--radius-object);
    --card-padding: var(--pad);
    --card-shadow: var(--shadow-object);
    --card-hover-shadow: var(--shadow-object-hover);
    --card-border: transparent;
    padding-block: var(--pad-y);
}
.sjm-card .hzo-card__title {
    font-family: var(--font-heading);
    font-size: var(--fs-card);
    line-height: 1.2;
    margin-block-end: var(--space-md);
}
.sjm-card .hzo-card__media {
    aspect-ratio: 3 / 2;
    border-radius: 0;
}
.sjm-card .hzo-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
/* The engine already ships the lift and the 1.05 media zoom on :has(a), and
   both already match the contract's one hover vocabulary — kept, retuned to
   this site's shadow rather than replaced. */
.sjm-card:focus-within {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
}
@media (max-width: 767px) {
    .sjm-card {
        --card-padding: var(--space-lg);
        padding-block: var(--space-40);
    }
}/* @end-treatment */

/* @treatment cta */
.sjm-cta {
    background: var(--brand-deep);
    color: var(--text-on-brand);
    border-radius: 0;
    padding-block: var(--section-pad-y);
    margin-inline: calc(50% - 50vw);
    padding-inline: var(--rail-gutter);
}
.sjm-cta__inner {
    max-width: var(--container-max-width);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
}
.sjm-cta__title {
    font-family: var(--font-heading);
    font-size: var(--fs-title);
    line-height: 1.07;
    margin: 0 0 var(--space-lg);
}
.sjm-cta__lead {
    font-size: var(--fs-lead);
    max-width: var(--measure-65);
    margin: 0 0 var(--space-2xl);
}
.sjm-cta__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-md);
}
/* @end-treatment */

/* @treatment steps */
/* MOTIF CARRIER — layered-planes. Each stage is a plane that overlaps the one
   before it and breaks the rail, so the sequence reads as built volumes
   stacking rather than as a numbered list. */
.sjm-steps {
    display: flex;
    flex-direction: column;
    position: relative;
    isolation: isolate;
}
.sjm-steps__plane {
    background: var(--surface);
    border-radius: var(--radius-object);
    padding: var(--pad-y) var(--pad);
    box-shadow: var(--shadow-object);
    position: relative;
}
.sjm-steps__plane + .sjm-steps__plane { margin-top: calc(var(--space-lg) * -1); }
.sjm-steps__plane:nth-child(even) {
    background: var(--bg-alt);
    margin-inline-start: calc(var(--pull-1) * -1);
    z-index: var(--z-above);
}
.sjm-steps__plane:nth-child(odd) { margin-inline-end: var(--pull-1); }
@media (max-width: 767px) {
    .sjm-steps__plane { margin-inline: 0; padding: var(--space-40) var(--space-lg); }
    .sjm-steps__plane + .sjm-steps__plane { margin-top: var(--space-md); }
}
/* @end-treatment */

/* ═══ SITE SKIN — chrome ══════════════════════════════════════════════════
   Footer + the two engine corrections chrome needs. Per docs/page-plans/
   _chrome.md (signed 2026-09-09). Skin, not treatment: the footer is not one
   of the eight signature families, so it carries no @treatment marker.
   ═══════════════════════════════════════════════════════════════════════ */

/* ENGINE CORRECTION 1 — the accent must not paint eyebrows.
   brand-dna.md restricts the accent to primary CTAs and ACTIVE states;
   _chrome.md 6 bans it on headings outright. Measured live 2026-09-09 as a
   rust "READY TO GET STARTED?" on the closing CTA band.

   TWO THINGS MADE THE FIRST ATTEMPT FAIL, both found by reading the rendered
   markup instead of assuming:
     1. the band is `.hzo-section--brand`, NOT `--dark`
     2. [hzo_final_cta] emits the colour as an INLINE style attribute
        (`<h2 class="hzo-eyebrow" style="color:var(--accent);">`), and an
        inline style outranks every stylesheet rule that is not !important

   So this is the narrow, sanctioned use of !important from quality-rules.md
   1.2 — a single property, on an `.hzo-*`-scoped selector, to beat a
   higher-precedence declaration we do not own. It is not a layout property and
   it is not on a bare element selector. The durable fix is an engine change so
   the band takes its eyebrow colour from a token; until then this is the
   client-layer correction. */
.hzo-section--brand .hzo-eyebrow,
.hzo-section--dark .hzo-eyebrow,
.sjm-footer .hzo-eyebrow {
    color: rgba(255, 255, 255, .72) !important;
}

/* ENGINE CORRECTION 2 — a bled element overhangs by one scrollbar width, so the
   page root must clip. `clip`, never `hidden`: hidden kills position:sticky
   inside it, which would take out this header (quality-rules.md 1.12, 1.29). */
body { overflow-x: clip; }

.sjm-footer {
    background: var(--brand-deep);
    color: var(--text-on-brand);
    padding-block-start: var(--section-pad-y);
}
.sjm-footer__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-2xl) var(--space-lg);
}
@media (min-width: 768px)  { .sjm-footer__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .sjm-footer__grid { grid-template-columns: 1.4fr 1fr 1fr 1.2fr; } }
/* The first column is identity and address, not links — the reference
   principle in _chrome.md 3. */
/* ONE CONTROL SHAPE FOR THE BAR — operator, 2026-09-10. Measured before:
   41.6px vs 36px tall, 11px vs 14px, tracked vs untracked. One set now. */
.sjm-header__tel,
.sjm-header .sjm-header__cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-sm);
    min-height: 44px;
    padding-block: 0;
    padding-inline: var(--space-lg);
    border-radius: 0;
    font-family: var(--font-body);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    text-decoration: none;
}
/* Secondary tier: stroke, no fill — tellable from the primary with the colour
   removed. Hidden below 1024 because the mobile menu carries the number. */
.sjm-header__tel {
    display: none;
    border: 2px solid currentColor;
    color: var(--sjm-nav-ink);
}
@media (min-width: 1024px) { .sjm-header__tel { display: inline-flex; } }
/* DEFECT FIX. The tel is an <a>, so an engine link-hover rule repainted it
   #354C7A on hover — 2.4:1 on this ground, and the reason it read as "blue and
   cant see". Full white plus a faint fill, so the state is unmistakable. */
.sjm-header__tel:hover {
    color: var(--text-on-brand) !important;
    background: rgba(255, 255, 255, .10);
}

.sjm-footer__brand { max-width: 34ch; }
/* Operator, 2026-09-10: much bigger. It was rendering 70x40, smaller than the
   body copy beside it. [hzo_footer_brand] emits height:40px as an INLINE style,
   which no stylesheet rule beats without !important - the same override the
   engine-correction block above uses, and for the same reason. The invert is
   the footer's ground: it is as dark as the bar, so the navy mark disappears
   on it exactly as it did up there. */
.sjm-footer__brand img {
    height: 96px !important;
    width: auto !important;
    max-width: 100%;
    filter: brightness(0) invert(1);
}
.sjm-footer .hzo-footer-heading {
    font-size: var(--fs-label);
    font-weight: 600;
    letter-spacing: var(--eyebrow-tracking);
    text-transform: uppercase;
    margin: 0 0 var(--space-lg);
}
.sjm-footer .hzo-footer-link { text-decoration: none; }
.sjm-footer .hzo-footer-link:hover { text-decoration: underline; }

/* The credentials line — the client's own headline qualification, on every
   page. A plain statement, never a badge. */
.sjm-footer__credentials {
    margin-top: var(--section-pad-y);
    border-top: 1px solid rgba(255, 255, 255, .12);
    padding-block: var(--space-lg);
}
.sjm-footer__credline {
    margin: 0;
    font-size: var(--fs-label);
    font-weight: 600;
    letter-spacing: var(--eyebrow-tracking);
    text-transform: uppercase;
}

/* The one seam on the site where two dark grounds meet: the closing CTA band
   is also deep, so a ground change cannot do the work and a hairline does. */
.sjm-footer__legal {
    border-top: 1px solid rgba(255, 255, 255, .12);
    padding-block: var(--space-lg);
}
.sjm-footer__legal-inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-md) var(--space-2xl);
    align-items: center;
    justify-content: space-between;
}
.sjm-footer__legal-links {
    display: flex;
    gap: var(--space-lg);
    flex-wrap: wrap;
}
.sjm-footer__descriptor {
    margin: var(--space-lg) 0 0;
    font-size: var(--fs-small);
    line-height: 1.6;
    color: rgba(255, 255, 255, .72);
    max-width: 34ch;
}

/* ENGINE CORRECTION 3 — the eyebrow, site-wide.
   The engine inks .hzo-eyebrow with --accent-text fleet-wide. brand-dna.md
   restricts the accent to primary CTAs and ACTIVE states and names headings as
   forbidden — and an eyebrow is an h1/h2. This is the THIRD place that one
   conflict surfaced (dark sections, the CTA band, now every section head), so
   it is fixed once at the source rather than per-band.

   The accent keeps one legal home: design-contract.md clause 9 permits an
   accent DOT before the eyebrow label. That is a small sanctioned presence in
   every section head without putting the accent on a heading, which is what
   the rule was protecting. */
.hzo-eyebrow { color: var(--text-muted); }
.hzo-eyebrow::before {
    content: "";
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-inline-end: var(--space-sm);
    border-radius: var(--radius-pill);
    background: var(--accent);
    vertical-align: middle;
}
.hzo-section--dark .hzo-eyebrow,
.hzo-section--brand .hzo-eyebrow,
.sjm-footer .hzo-eyebrow { color: rgba(255, 255, 255, .72); }

/* ═══ SITE SKIN — auto-inserted internal links ════════════════════════════
   The Internal Links Manager plugin wraps keywords in links anywhere in copy;
   includes/link-guard.php tags each one .sjm-autolink. Left to the engine's link
   colour they render --brand-text navy, which vanished on dark grounds (operator,
   2026-09-15: the homepage hero, on a dark photograph). They now take the colour
   of the text they sit in - already legible on every ground by design - and an
   underline in that same colour marks them as links (contract clause 17: inline
   links in body copy are the ones that carry an underline). :visited is named
   because the engine's a:visited paints --brand-text too. */
a.sjm-autolink,
a.sjm-autolink:visited {
    color: inherit;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-decoration-color: currentColor;
    text-underline-offset: 3px;
}
a.sjm-autolink:hover { text-decoration-thickness: 2px; }
a.sjm-autolink:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
