/* ============================================================
   markhooper.cirrusvps.info — client.css
   Single stylesheet for the Mark Hooper Tree Care front-end.

   Typefaces:  Newsreader — soft-contrast serif, all headings
               Manrope   — sans, body copy and UI
   Loaded from Google Fonts in index.php.
   ============================================================ */

/* ---------- 1. Tokens -------------------------------------- */

:root {
    /* Brand palette — "modernised green" */
    --brand:        #2E6F4E;
    --brand-dark:   #1F4E37;
    --brand-light:  #E8F2EB;
    --deep:         #16281E;

    /* Surfaces and text — dark forest ground, light type */
    --bg:           #12291E;   /* dark forest green — site background */
    --surface:      #183626;
    --ink:          #F2F7F4;
    --ink-soft:     #B4C7BC;
    --rule:         rgba(255, 255, 255, 0.12);

    /* Headings have their own ladder rather than borrowing the body type
       scale. Sizes match the existing markhoopertreecare.co.uk site, whose
       headings take browser defaults at a 16px root: h1 32px, h2 24px,
       h3 18.7px, h4 16px. Each shrinks a little on narrow screens.
       --heading-scale multiplies the lot, so the whole ladder still tunes
       from one number. */
    --heading-scale: 1.15;

    --h1-size:      clamp(1.75rem, 1.45rem + 1.20vw, 2.00rem);
    --h2-size:      clamp(1.35rem, 1.20rem + 0.60vw, 1.50rem);
    --h3-size:      clamp(1.08rem, 1.02rem + 0.25vw, 1.17rem);
    --h4-size:      1.00rem;
    --h5-size:      0.94rem;
    --h6-size:      0.88rem;

    /* Heading colour — a lighter shade of the background green
       (--bg #12291E). On the dark ground that means a pale tint; main
       re-points it to a mid green that still clears contrast on white. */
    --heading:      var(--brand-light);

    /* Links default to white. main re-points this for its white ground,
       where white on white would be invisible. */
    --link:         #FFFFFF;
    --link-hover:   var(--accent);

    /* Accent tuned for legibility on the dark ground */
    --accent:       #86D6A6;
    --accent-dark:  #5FBE87;

    /* Buttons follow whichever surface they sit on (see `main` below) */
    --btn-bg:       var(--accent);
    --btn-bg-hover: var(--accent-dark);
    --btn-ink:      var(--deep);

    /* Typefaces */
    --font-body:    "Manrope", ui-sans-serif, system-ui, -apple-system,
                    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-display: "Newsreader", ui-serif, Georgia, "Times New Roman",
                    Times, serif;

    /* Fluid type scale — shrinks on small screens automatically.
       Trimmed 8%, then a further 10%, from the original values; the ratio
       between steps is unchanged, so the hierarchy holds. */
    --step--1:      clamp(0.68rem, 0.67rem + 0.13vw, 0.77rem);
    --step-0:       clamp(0.83rem, 0.79rem + 0.16vw, 0.94rem);
    --step-1:       clamp(0.99rem, 0.93rem + 0.33vw, 1.20rem);
    --step-2:       clamp(1.19rem, 1.08rem + 0.58vw, 1.58rem);
    --step-3:       clamp(1.43rem, 1.24rem + 0.95vw, 2.07rem);
    --step-4:       clamp(1.71rem, 1.40rem + 1.53vw, 2.74rem);
    --step-5:       clamp(2.06rem, 1.58rem + 2.44vw, 3.60rem);

    /* Spacing */
    --space-xs:     0.5rem;
    --space-sm:     0.875rem;
    --space-md:     1.5rem;
    --space-lg:     2.5rem;
    --space-xl:     4rem;
    --space-2xl:    6rem;

    /* Layout */
    --measure:      68ch;   /* comfortable reading width */
    --sleeve:       1500px; /* every master tag centres its content here */
    --sleeve-narrow: 950px; /* sub-footer: a shorter measure for centred copy */
    --sleeve-pad:   15px;
    --main-pad-top: var(--space-md);  /* breathing room above page content */

    /* Banner aspect ratio, expressed as the padding-bottom percentage
       of its own width: height / width x 100.
       16:9 = 56.25%   21:9 = 42.857%   3:1 = 33.333%   4:1 = 25% */
    --banner-ratio: 42.857%;

    /* Home page video hero. 52.75% is the video's own frame shape
       (1600 x 844, from a 4096 x 2160 master) — the same depth the
       existing markhoopertreecare.co.uk site gets from height:auto,
       and the one value at which cover crops nothing. */
    --video-ratio:  52.75%;
    /* Hero logo caps, both trimmed 20% (120 -> 96, 340 -> 272). The logo
       is 300x96, so at these sizes the WIDTH cap is what actually binds on
       desktop — both have to move together for the rendered size to change. */
    --hero-logo-h:  96px;
    --hero-logo-w:  272px;
    --radius:       10px;

    /* Fixed header */
    --header-h:     84px;
    /* Logo cap height: bar height less padding, then trimmed twice
       by 10% (0.9 x 0.9 = 0.81). */
    --logo-h:       calc((var(--header-h) - 28px) * 0.81);
    /* The site background (--bg, #12291E) at the same 50% opacity. */
    --header-bg:    rgba(18, 41, 30, 0.5);
    --header-blur:  14px;
    --z-header:     1000;
    --submenu-bg:   #10241A;

    /* Header typography — same size as body copy inside main. Colour is
       set on `color` (not the opacity property) so the group caret, which
       draws with currentColor, inherits it too. */
    --header-text:  var(--step-0);   /* matches body copy inside main */
    --header-ink:   #FFFFFF;   /* solid, no alpha */
    --header-ink-hover: #FFFFFF;
}

/* ---------- 2. Reset / base -------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
    /* Tells the UA (and Safari's toolbar tint) this is a dark page. */
    color-scheme: dark;
    background: var(--bg);
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    /* Stop in-page anchors landing underneath the fixed header. */
    scroll-padding-top: var(--header-h);
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: var(--step-0);
    font-weight: 400;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

img,
picture,
video,
canvas,
svg {
    display: block;
    max-width: 100%;
    height: auto;
}

input,
button,
textarea,
select { font: inherit; color: inherit; }

/* ---------- 3. Headings — serif ---------------------------- */

h1, h2, h3, h4, h5, h6 {
    margin: 0 0 var(--space-sm);
    font-family: var(--font-display);
    font-weight: 600;
    font-optical-sizing: auto;
    line-height: 1.12;
    letter-spacing: -0.015em;
    text-wrap: balance;
    color: var(--heading);
}

h1 { font-size: calc(var(--h1-size) * var(--heading-scale)); font-weight: 700; }
h2 { font-size: calc(var(--h2-size) * var(--heading-scale)); }
h3 { font-size: calc(var(--h3-size) * var(--heading-scale)); }
h4 { font-size: calc(var(--h4-size) * var(--heading-scale)); }
h5 { font-size: calc(var(--h5-size) * var(--heading-scale)); }
h6 {
    font-size: calc(var(--h6-size) * var(--heading-scale));
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* Newsreader carries a single optical-size axis, driven by
   font-optical-sizing: auto on the heading rule above — no explicit
   font-variation-settings needed. Its softer curves want a touch less
   negative tracking than Fraunces did. */
h1, h2 { letter-spacing: -0.008em; }

/* ---------- 4. Body copy ----------------------------------- */

p,
ul,
ol,
dl,
blockquote,
figure,
table { margin: 0 0 var(--space-md); }

/* No reading measure: body copy runs the full width of the sleeve.
   text-wrap: pretty is kept — it still balances the last line and avoids
   orphans, which matters more, not less, at a long measure. */
p { text-wrap: pretty; }

a {
    color: var(--link);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
    transition: color 0.15s ease;
}
a:hover,
a:focus-visible { color: var(--link-hover); }

strong, b { font-weight: 700; }

small { font-size: var(--step--1); color: var(--ink-soft); }

blockquote {
    padding-left: var(--space-md);
    border-left: 3px solid var(--accent);
    color: var(--ink-soft);
    font-style: italic;
}

hr {
    height: 1px;
    margin: var(--space-lg) 0;
    border: 0;
    background: var(--rule);
}

:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
    border-radius: 3px;
}

/* ---------- 4b. Editor image alignment (Hydra classes) ----- */

/* Hydra's editor tags a floated image with .img_left / .img_right and a
   full-width one with .img_full — see the toolbar buttons in
   hydra/tinymce/js/tinymce/tiny.hydra.js. Hydra only applies the class:
   its own manual says "your website stylesheet decides how the float
   looks", so the front-end half has to live here or the classes do
   nothing outside the editor.

   Values match hydra/styles/tiny.css exactly — same 20px gutter — because
   TinyMCE loads that file as its content_css. Diverging here would make
   the editor preview stop matching the published page. */

.img_left {
    float: left;
    margin: 0 20px 20px 0;
}

.img_right {
    float: right;
    margin: 0 0 20px 20px;
}

/* Note this only caps the width, exactly as Hydra's does — it does not
   stretch a small image up to the container. */
.img_full {
    max-width: 100%;
    height: auto;
    margin: 0 0 20px 0;
}

/* Editor images on small screens.

   These used to be forced to 100% and un-floated, which made a photograph
   dropped into a paragraph the loudest thing on a phone. They are half the
   width of the column instead.

   At half width the float earns its place again — copy wraps down the side
   properly, which is what it could not do at 100% — so .img_left and
   .img_right keep floating. Everything else is centred, because a 50%
   image sitting flush left with empty space beside it reads as a mistake
   rather than a choice.

   Hydra's own editor stylesheet only drops the side margins here, so the
   editor preview and the published page part company below 800px. That is
   accepted: assets/editor/tiny.css is shared ground overwritten by
   updater.php, and this is a front-end decision. */
@media (max-width: 800px) {
    .img_left,
    .img_right,
    .img_full,
    main :is(p, li, td, blockquote, figure) > img {
        width: 50%;
        height: auto;
    }

    /* Tighter gutter than the 20px desktop one — at half width there is
       less room to give away. */
    .img_left  { margin: 0 14px 14px 0; }
    .img_right { margin: 0 0 14px 14px; }

    .img_full,
    main :is(p, li, td, blockquote, figure) > img {
        margin-inline: auto;
    }
}

/* Rounded corners on images placed in page content through the editor.
   TinyMCE drops an inserted image inside a paragraph and tags a floated
   one with .img_left / .img_right, so both shapes are covered.

   Scoped by parent rather than applied to every img in main, so it cannot
   reach the images this stylesheet places itself: a service tile is
   <a> > img and carries its radius on the tile, and rounding the image
   inside it as well would show a lighter corner against the clipped one.

   Not mirrored into assets/editor/tiny.css deliberately — it is a
   front-end finish, and Hydra's editor stylesheet is shared ground that
   updater.php overwrites. */
main :is(p, li, td, blockquote, figure) > img,
main .img_left,
main .img_right,
main .img_full {
    border-radius: var(--radius);
}

/* A float taller than the text beside it would otherwise hang out of the
   bottom of its container and collide with whatever follows. Contain them
   at the point page content is dropped in. */
main > .sleeve::after {
    content: "";
    display: table;
    clear: both;
}

/* ---------- 5. Layout helpers ------------------------------ */

/* The sleeve is the single centring container. Every master tag
   (header, main, footer, section...) holds one, so the whole site
   lines up on the same left and right edges. */
.sleeve {
    width: 100%;
    max-width: var(--sleeve);
    margin-inline: auto;
    padding: var(--sleeve-pad);
}

/* Sections own their vertical rhythm, so their sleeve only pads
   left and right. */
.section > .sleeve { padding-block: 0; }

/* ---------- Home intro strip -------------------------------- */

/* Sits between the video and main on the home page. Full browser width
   with a very light green ground; the sleeve keeps its content on the
   site's usual left and right edges. */
.home-top {
    width: 100%;
    background: var(--brand-light);

    /* Light surface, so the ink, heading and link tokens are re-pointed
       the same way main does — the page defaults are light-on-dark and
       would be invisible here. */
    --ink:          #16281E;
    --ink-soft:     #4C5B53;
    --rule:         #D5E3DA;
    --heading:      #1F4E37;
    --link:         var(--brand);
    --link-hover:   var(--brand-dark);

    color: var(--ink);
}

.home-top__inner {
    padding-block: var(--space-lg);
    text-align: center;
}

/* Keep the reading measure so lines do not run the full 1500px, but
   centre the column rather than letting it hang on the left — otherwise
   centred text sits centred inside a left-aligned block. */
.home-top__inner p {
    max-width: var(--measure);
    margin-inline: auto;
}
.home-top__inner img { margin-inline: auto; }
.home-top__inner > :last-child { margin-bottom: 0; }

/* ---------- Pre-footer strip -------------------------------- */

/* Sits between main and the footer on every page, on the same light green
   ground as the home page intro strip. Unlike every other master tag it
   carries no sleeve, so its content runs the full browser width. */
.prefooter {
    width: 100%;
    background: var(--brand-light);

    /* Light surface: re-point ink, heading, rule and link tokens the same
       way .home-top does — the page defaults are light-on-dark and would
       be invisible here. */
    --ink:          #16281E;
    --ink-soft:     #4C5B53;
    --rule:         #D5E3DA;
    --heading:      #1F4E37;
    --link:         var(--brand);
    --link-hover:   var(--brand-dark);

    color: var(--ink);
}

/* No max-width and no auto margins: this is the full-width strip. Only the
   same left/right padding the sleeve uses, so text never touches the
   browser edge. */
.prefooter__inner {
    padding: var(--space-lg) var(--sleeve-pad);
    text-align: center;
}

.prefooter__inner > :last-child { margin-bottom: 0; }

/* ---------- Accreditation logos ({{pre-footer}}) ------------- */

/* Matches the treatment on the existing markhoopertreecare.co.uk site: a
   level row of marks, greyed back so they read as credentials rather than
   compete with the page, returning to full colour on hover.

   Their background-color is deliberately not copied — the prefooter strip
   this sits in already supplies its own ground. */
.companies {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--space-md) var(--space-lg);
}

/* A fixed height with automatic width is what makes the row sit level: the
   source logos range from 350x98 to 350x384, so anything driven off width
   would leave them wildly different heights. */
.companies img {
    height: 70px;
    width: auto;
    max-width: 100%;
    filter: grayscale(100%);
    opacity: 0.7;
    transition: filter 0.15s ease, opacity 0.15s ease;
}

.companies a:hover img,
.companies a:focus-visible img {
    filter: none;
    opacity: 1;
}

/* ---------- Main ------------------------------------------- */

/* Full browser width with a white ground, holding the sleeve that keeps
   the content centred. Flush to the bottom of the banner — no margin,
   no gap. */
main {
    width: 100%;
    padding-top: var(--main-pad-top);
    background: #FFFFFF;

    /* Light surface: re-point the ink, rule and accent tokens so
       everything inside main reads against white instead of the dark
       forest ground the rest of the page uses. */
    --ink:          #16281E;
    --ink-soft:     #4C5B53;
    --rule:         #E2E8E4;
    --surface:      #F7F9F8;
    --accent:       var(--brand);
    --accent-dark:  var(--brand-dark);
    --heading:      #4A9E72;             /* much lighter green — 3.3:1 on white */
    --link:         var(--brand);        /* white would vanish on white */
    --link-hover:   var(--brand-dark);
    --btn-bg:       var(--brand);
    --btn-bg-hover: var(--brand-dark);
    --btn-ink:      #FFFFFF;

    color: var(--ink);
}

/* A slight lift on main's headings. Kept low-opacity and tight so it
   reads as depth rather than a drop shadow. */
main :is(h1, h2, h3, h4, h5, h6) {
    text-shadow: 0 1px 2px rgba(18, 41, 30, 0.20);
}

/* With no banner there is nothing in the flow above main, and the header
   is fixed (so out of flow) — main would slide underneath it. Push it
   down by exactly the header's height so it sits flush beneath. */
.main--no-banner {
    /* Clears the fixed header, plus the same breathing room the hero
       pages get — so the gap above the content reads the same either way. */
    padding-top: calc(var(--header-h) + var(--main-pad-top));
}

/* ---------- Footer ----------------------------------------- */

/* Sits directly under main, on the page's dark green ground, so it picks
   up the global (light-on-dark) tokens rather than main's white ones. */
.site-footer {
    width: 100%;
    border-top: 1px solid var(--rule);
}

/* Columns at 25% / 25% / 50%. Expressed in fr rather than percentages so
   the gaps are subtracted first — 25% + 25% + 50% plus two gaps would
   overflow the row. minmax(0, ...) rather than a bare fr so a long
   unbroken string cannot widen its column past its share. */
.site-footer__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 2fr);
    gap: var(--space-lg);
    padding-block: var(--space-xl);
}

.site-footer__block {
    min-width: 0;
    font-size: var(--step--1);
    color: var(--ink-soft);
    /* Centred at every width, not just on phones. The lists inside already
       carry list-style: none and no padding, so nothing is left hanging
       off the centre line. */
    text-align: center;
}
.site-footer__block > :last-child { margin-bottom: 0; }
.site-footer__block p { max-width: none; }
.site-footer__block :is(h1, h2, h3, h4, h5, h6),
.site-footer__title {
    font-size: var(--step-0);
    margin-bottom: 0.6em;
    color: var(--heading);
}

/* Links inside any footer column — colour inherited from the default. */
.site-footer__block a { text-decoration: none; }
.site-footer__block a:hover,
.site-footer__block a:focus-visible { text-decoration: underline; }
.site-footer__block ul {
    padding: 0;
    list-style: none;
}
.site-footer__block li + li { margin-top: 0.35em; }

/* ---------- Sub-footer ------------------------------------- */

/* Sits under the footer as a black strip, edge to edge, with its sleeve
   holding the content on the site's usual left and right edges. */
.site-subfooter {
    width: 100%;
    background: #000000;
    border-top: 1px solid var(--rule);
}

.site-subfooter__inner {
    /* Narrower than the site sleeve. Same class, so this wins on source
       order rather than specificity — it must stay after .sleeve. */
    max-width: var(--sleeve-narrow);

    padding-block: var(--space-md);
    font-size: var(--step--1);
    color: var(--ink-soft);
    text-align: center;
}
/* Centred, so the global reading-width cap has to come off. */
.site-subfooter__inner p {
    max-width: none;
    margin-bottom: 0.4em;
}
.site-subfooter__inner > :last-child { margin-bottom: 0; }

/* Build credit, sitting under the disclaimer. */
.site-credit {
    margin-top: var(--space-sm);
    font-size: var(--step--1);
}
.site-credit a { text-decoration: none; }
.site-credit a:hover,
.site-credit a:focus-visible { text-decoration: underline; }

/* ---------- Social button ---------------------------------- */

/* Facebook's own brand blue, with the f mark drawn inline as a data URI
   so there is no external request and nothing for TinyMCE to strip — the
   CMS block only has to carry a plain <a class="btn-facebook">.
   Written as a.btn-facebook and placed after the footer rules so it beats
   .site-footer__block a on source order rather than needing !important. */
a.btn-facebook {
    display: inline-flex;
    align-items: center;
    gap: 0.6em;
    padding: 0.7em 1.15em;
    border-radius: var(--radius);
    background: #1877F2;
    color: #FFFFFF;
    font-size: var(--step--1);
    font-weight: 700;
    line-height: 1.2;
    text-decoration: none;
    transition: background 0.15s ease;
}
a.btn-facebook::before {
    content: "";
    flex: 0 0 auto;
    width: 1.35em;
    height: 1.35em;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='12' fill='%23fff'/%3E%3Cpath fill='%231877F2' d='M15.1 12.6h-2.2V20h-3v-7.4H8.3V10h1.6V8.5c0-2 .9-3.4 3.3-3.4h2v2.6h-1.3c-.9 0-1 .4-1 1V10h2.3l-.2 2.6z'/%3E%3C/svg%3E") center / contain no-repeat;
}
a.btn-facebook:hover,
a.btn-facebook:focus-visible {
    background: #0F5FD1;
    color: #FFFFFF;
    text-decoration: none;
}

/* ---------- Two-column page ---------------------------------- */

/* Used on pages that carry a form: copy left, form right, equal halves.
   align-items: start so the shorter column does not stretch. */
.page-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-lg);
    align-items: start;
}

/* Inside a half-width column the form should fill it rather than keep
   its own standalone reading width. */
.page-split__aside .form,
.page-split__aside .form__gdpr {
    max-width: none;
}
.page-split__aside .form { margin-top: 0; }

/* ---------- Forms ------------------------------------------ */

/* Rendered on whichever page a form is linked to via
   forms.form_content_id. Lives inside main, so it inherits main's
   light-surface tokens. */
.form {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
    max-width: 40rem;
    margin-block: var(--space-lg);
}

.form__field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.form__label,
.form__group legend {
    font-size: var(--step--1);
    font-weight: 700;
    color: var(--ink);
}

.form__req { color: #B3261E; margin-left: 0.15em; }

.form__input,
.form__select {
    width: 100%;
    padding: 0.7em 0.85em;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: #FFFFFF;
    color: var(--ink);
    font-size: var(--step-0);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.form__input:focus,
.form__select:focus {
    border-color: var(--brand);
    box-shadow: 0 0 0 3px rgba(46, 111, 78, 0.18);
    outline: none;
}

.form__textarea {
    min-height: 8em;
    resize: vertical;
}

.form__group {
    margin: 0;
    padding: 0;
    border: 0;
}

.form__check {
    display: flex;
    align-items: flex-start;
    gap: 0.55em;
    font-size: var(--step-0);
    cursor: pointer;
}
.form__check input { margin-top: 0.25em; flex: 0 0 auto; }

.form__actions { margin-top: var(--space-xs); }
.form__submit { align-self: flex-start; }

/* Honeypot: off-screen rather than display:none, which some bots skip. */
.form__hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* Response from submit.php. Empty until there is something to announce. */
.form__msg {
    margin: 0;
    font-size: var(--step-0);
    font-weight: 600;
}
.form__msg:empty { display: none; }
.form__msg.is-ok    { color: var(--brand-dark); }
.form__msg.is-error { color: #B3261E; }

/* {{gdpr-statement}}, shown beneath every form. */
.form__gdpr {
    max-width: 40rem;
    margin-block: var(--space-md) var(--space-lg);
    padding-top: var(--space-md);
    border-top: 1px solid var(--rule);
    color: var(--ink-soft);
    font-size: var(--step--1);
}
.form__gdpr :is(h1, h2, h3, h4, h5, h6) {
    font-size: var(--step-0);
    margin-bottom: 0.4em;
}
.form__gdpr p { max-width: none; }

/* ---------- Home video hero -------------------------------- */

/* Full browser width, permanent on the home page. Same padding-bottom
   ratio technique as the banner, so the box keeps its shape at any
   window size and nothing reflows when the video finishes loading. */
.video-hero {
    position: relative;
    width: 100%;
    height: 0;
    padding-bottom: var(--video-ratio);
    overflow: hidden;

    /* Poster shows through until the first frame paints, and stands in
       for the video entirely under reduced-motion. */
    background-color: var(--deep);
    background-image: var(--hero-poster);
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
}

.video-hero__media {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;   /* fills the box, crops the overflow */
}

/* --- Floating overlay --- */

.video-hero__overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;      /* centred vertically */
    justify-content: center;  /* centred horizontally */
    text-align: center;

    /* Scrim: keeps the copy legible over any frame of the video, and
       gives the fixed header something to sit against at the top. */
    background: linear-gradient(
        to bottom,
        rgba(0, 0, 0, 0.55) 0%,
        rgba(0, 0, 0, 0.25) 45%,
        rgba(0, 0, 0, 0.60) 100%
    );
}

.video-hero__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-sm);
}

/* The overlay's markup comes from the {{video-overlay}} HTML block, so
   these are element selectors rather than BEM classes — whatever the
   editor writes in TinyMCE gets styled. */
.video-hero__inner > * { margin: 0; }

/* {{logo}} again, above the overlay copy — larger than the header's. */
.video-hero__logo {
    width: auto;
    height: auto;
    max-height: var(--hero-logo-h);
    max-width: min(var(--hero-logo-w), 56vw);
    margin-bottom: var(--space-xs);
    object-fit: contain;
    filter: drop-shadow(0 2px 14px rgba(0, 0, 0, 0.5));
}

.video-hero__inner :is(h1, h2, h3) {
    font-size: var(--step-4);   /* display size, independent of the ladder */
    color: var(--heading);
    text-shadow: 0 2px 18px rgba(0, 0, 0, 0.45);
}

.video-hero__inner p {
    max-width: 46ch;   /* overrides the global measure for centred copy */
    font-size: var(--step-1);
    color: rgba(255, 255, 255, 0.88);
    text-shadow: 0 1px 12px rgba(0, 0, 0, 0.45);
}

.video-hero__inner a { color: #FFFFFF; }

/* Reduced motion: no autoplaying footage. The poster on .video-hero
   stays visible in its place. */
@media (prefers-reduced-motion: reduce) {
    .video-hero__media { display: none; }
}

/* ---------- Banner ----------------------------------------- */

/* Full width of the browser window — the one part of the site that
   breaks out of the sleeve. Sits directly below the header in the
   document but flush to the top of the page, so the fixed header
   floats over its upper edge and the backdrop blur has something to
   work against. */
.banner {
    width: 100%;
}

.banner__media {
    width: 100%;

    /* Aspect ratio held by the padding-bottom percentage trick: vertical
       padding resolves against the element's own WIDTH, so the box keeps
       its ratio at any window size. Height must be 0 — the padding is
       the whole box. */
    height: 0;
    padding-bottom: var(--banner-ratio);

    /* The image is the banner's background: centred, and scaled to cover
       the ratio box in both directions, cropping the overflow. */
    background-color: var(--surface);
    background-image: var(--banner-image);
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
}

.section { padding-block: var(--space-2xl); }
.section--tight { padding-block: var(--space-xl); }
.section--alt { background: var(--surface); }  /* lifted dark panel */

.stack > * + * { margin-top: var(--space-md); }

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

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

/* ---------- 6. Site header (fixed, full-bleed) ------------- */

.site-header {
    position: fixed;
    inset: 0 0 auto 0;          /* top / left / right — edge to edge */
    z-index: var(--z-header);
    width: 100%;
    min-height: var(--header-h);
    display: flex;
    align-items: center;
    background: var(--header-bg);
    color: #FFFFFF;

    /* Everything scrolling beneath the bar is blurred. */
    -webkit-backdrop-filter: blur(var(--header-blur)) saturate(140%);
    backdrop-filter: blur(var(--header-blur)) saturate(140%);
}

/* No backdrop-filter (older Firefox, some Android): fall back to a
   solid-enough bar so white text stays legible over any content. */
@supports not ((backdrop-filter: blur(1px)) or
               (-webkit-backdrop-filter: blur(1px))) {
    .site-header { background: rgba(18, 41, 30, 0.88); }
}

/* No banner and no video: main's white ground sits behind the bar. The
   base ink stays #FFFFFF as everywhere else; only the hover and active
   colours are re-pointed so they read against the lighter bar.

   The bar is 50% #12291E over whatever scrolls beneath, so on these pages
   it composites to roughly #88948E — a mid tone. Against that, a bright
   mint reads better than the dark --brand this used to carry (2.35:1
   against 1.91:1), as well as being the livelier colour. Note the bar's
   own translucency caps what is achievable here: even pure white manages
   only 3.15:1, so no value clears 4.5:1 without reworking the bar itself. */
.site-header--plain {
    --header-ink-hover: #12291E;
    --accent:           #9BF0C4;
}

.site-header__inner {
    display: flex;
    align-items: flex-end;   /* header text sits on the logo's bottom edge */
    justify-content: space-between;
    gap: var(--space-md);
}

/* Left group: {{logo}} then the desktop menu, both flush left. */
.site-header__primary {
    display: flex;
    align-items: flex-end;
    gap: clamp(1.25rem, 3vw, 2.75rem);
    min-width: 0;
}

/* Right group: {{telephone}} and {{email}}. */
.site-header__secondary {
    display: flex;
    align-items: flex-end;
    gap: clamp(1rem, 2.5vw, 2rem);
    margin-left: auto;
}

/* --- Logo ({{logo}}) --- */

.site-logo {
    display: block;
    flex: 0 0 auto;
    line-height: 0;
}
.site-logo img {
    width: auto;
    height: auto;
    max-height: var(--logo-h);
    max-width: 220px;
    object-fit: contain;
}

/* --- Desktop menu --- */

.site-nav__list {
    display: flex;
    align-items: flex-end;
    gap: clamp(0.9rem, 2vw, 2rem);
    margin: 0;
    padding: 0;
    list-style: none;
}

.site-nav__item { position: relative; }

/* Top-level items. A group is a <button> rather than an <a> — it opens the
   panel instead of going anywhere — so both are styled together here and
   the two are indistinguishable in the bar, apart from the caret. */
.site-nav__list > li > a,
.site-nav__group {
    display: inline-flex;
    align-items: center;   /* caret centres on the text, not the box */
    gap: 0.4em;
    padding-top: 0.35em;
    padding-bottom: 0.18em;
    line-height: 1;
    color: var(--header-ink);
    font-size: var(--header-text);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    border-bottom: 2px solid transparent;
    transition: color 0.15s ease, border-color 0.15s ease;
}

/* Undo the parts of a button that a link does not have. font: inherit is
   already set globally on button; these are the rest. */
.site-nav__group {
    padding-left: 0;
    padding-right: 0;
    border-top: 0;
    border-left: 0;
    border-right: 0;
    background: none;
    cursor: pointer;
    font-family: inherit;
}

.site-nav__list > li > a:hover,
.site-nav__list > li > a:focus-visible,
.site-nav__list > li > a[aria-current="page"],
.site-nav__group:hover,
.site-nav__group:focus-visible {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* --- Group caret --- */

.site-nav__caret {
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid currentColor;
    transition: transform 0.15s ease;
}
.site-nav__item--has-sub:hover > .site-nav__group .site-nav__caret,
.site-nav__item--has-sub:focus-within > .site-nav__group .site-nav__caret,
.site-nav__item--has-sub.is-open > .site-nav__group .site-nav__caret {
    transform: rotate(180deg);
}

/* --- Sub menu --- */

.site-nav__sub {
    /* The panel is always dark, so it re-establishes light ink even when
       the bar above it has flipped to dark. */
    --header-ink:       #FFFFFF;
    --header-ink-hover: #FFFFFF;
    --accent:           #86D6A6;

    position: absolute;
    top: 100%;
    left: 0;
    z-index: 1;
    min-width: 220px;
    margin: 0;
    padding: 0.4rem 0;
    list-style: none;

    background: var(--submenu-bg);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);

    /* Hidden but still laid out, so the reveal can animate and the
       panel keeps its width. visibility keeps it out of the tab order. */
    opacity: 0;
    visibility: hidden;
    transform: translateY(6px);
    transition: opacity 0.15s ease, transform 0.15s ease,
                visibility 0s linear 0.15s;
}

/* Hover, keyboard focus, and the JS click state all open it. */
.site-nav__item--has-sub:hover > .site-nav__sub,
.site-nav__item--has-sub:focus-within > .site-nav__sub,
.site-nav__item--has-sub.is-open > .site-nav__sub {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
}

/* Bridges the gap between the bar and the panel so the pointer can
   travel down without the sub menu closing under it. */
.site-nav__item--has-sub > .site-nav__sub::before {
    content: "";
    position: absolute;
    inset: -14px 0 auto 0;
    height: 14px;
}

.site-nav__sub a {
    display: block;
    padding: 0.55rem 1.1rem;
    color: var(--header-ink);
    font-size: calc(var(--header-text) * 0.95);
    font-weight: 500;
    line-height: 1.35;
    text-decoration: none;
    transition: background 0.12s ease, color 0.12s ease;
}
.site-nav__sub a:hover,
.site-nav__sub a:focus-visible,
.site-nav__sub a[aria-current="page"] {
    background: rgba(255, 255, 255, 0.07);
    color: var(--accent);
}

/* --- Telephone + email ({{telephone}}, {{email}}) --- */

.site-contact {
    line-height: 1;
    padding-bottom: 0.2em;
    color: var(--header-ink);
    font-size: var(--header-text);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.15s ease;
}
.site-contact:hover,
.site-contact:focus-visible { color: var(--accent); }

/* The phone number is the one thing in the bar that should not recede.
   Explicitly white on every page — it previously followed
   --header-ink-hover, which the hero-less header re-points to dark. */
.site-contact--tel {
    color: #FFFFFF;
    font-weight: 700;
}

/* ---------- 7. Buttons ------------------------------------- */

.btn {
    display: inline-block;
    padding: 0.8em 1.6em;
    border: 2px solid var(--btn-bg);
    border-radius: var(--radius);
    background: var(--btn-bg);
    color: var(--btn-ink);
    font-family: var(--font-body);
    font-size: var(--step-0);
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease,
                color 0.15s ease;
}
.btn:hover,
.btn:focus-visible {
    background: var(--btn-bg-hover);
    border-color: var(--btn-bg-hover);
    color: var(--btn-ink);
}

.btn--ghost {
    background: transparent;
    color: var(--btn-bg);
}
.btn--ghost:hover,
.btn--ghost:focus-visible {
    background: var(--btn-bg);
    color: var(--btn-ink);
}

/* ---------- Reviews ---------------------------------------- */

/* Rendered wherever an editor drops {{google-reviews}} into page copy.
   Sits inside main, so it inherits main's white ground and its
   re-pointed ink / accent tokens. */
.reviews {
    display: block;
    clear: both;               /* never sit beside a floated image above */
    margin-top: var(--space-lg);
    padding: var(--space-lg) var(--space-md);
    border-radius: var(--radius);
    background: var(--brand-light);

    /* Light green surface, so the ink, heading, rule and link tokens are
       re-pointed exactly as .home-top does for the same ground — main's
       white-surface values would not all hold up here. */
    --ink:          #16281E;
    --ink-soft:     #4C5B53;
    --rule:         #D5E3DA;
    --heading:      #1F4E37;
    --link:         var(--brand);
    --link-hover:   var(--brand-dark);

    /* Cards lift off the green as white, rather than main's near-white
       #F7F9F8 which would barely register against it. */
    --surface:      #FFFFFF;

    /* An unfilled star at --rule would all but vanish on this ground, and
       the empty half of a rating has to stay readable for the filled half
       to mean anything. */
    --star-empty:   #B7CCC0;

    color: var(--ink);
    float: none;
    width: 100%;
}

.reviews__head {
    text-align: center;
    margin-bottom: var(--space-lg);
}

.reviews__title { margin-bottom: var(--space-xs); }

/* Sits directly under the heading, above the rating summary. */
.reviews__link {
    max-width: none;
    margin-bottom: var(--space-xs);
}

/* Stars, score and count on one line, wrapping together rather than
   breaking mid-phrase. */
.reviews__summary {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: center;
    gap: var(--space-xs);
    max-width: none;
    margin-bottom: 0;
}

.reviews__score {
    font-weight: 800;
    font-size: var(--step-1);
    color: var(--ink);
}

.reviews__count { color: var(--ink-soft); }

/* ---------- Stars ---------- */

.stars {
    display: inline-flex;
    gap: 0.1em;
    font-size: var(--step-1);
    line-height: 1;
    /* Unearned stars are the same glyph held back to a faint tint, so the
       five always occupy the same width whatever the rating. */
    color: var(--star-empty, var(--rule));
}

.stars--sm { font-size: var(--step-0); }

/* #F5A623 — the familiar review-star amber, which clears 3:1 against
   white as a non-text graphic. */
.stars__star--on { color: #F5A623; }

/* ---------- Grid ---------- */

/* Auto-fitting, so however many reviews the block holds the columns
   settle themselves — no fixed count to keep in step with the content. */
.reviews__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
    /* Every row the same height, so a short review and a long one produce
       identically sized cards rather than a ragged grid. Combined with the
       line clamp below, that makes all the cards match. */
    grid-auto-rows: 1fr;
    align-items: stretch;
    gap: var(--space-md);
    list-style: none;
    margin: 0;
    padding: 0;
}

.review {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    height: 100%;              /* fill the equal-height row */
    padding: var(--space-md);
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

.review__head {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
}

/* The sprite holding the Google mark. Never displayed itself — but kept
   as a zero-size box rather than display:none, which in some browsers
   stops <use> resolving against it. */
.reviews__sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* Google's mark, shown once per card to attribute the review. Sized in
   ems so it tracks the author name beside it. */
.review__logo {
    flex: 0 0 auto;
    width: 1.25em;
    height: 1.25em;
}

.review__author {
    font-weight: 700;
    color: var(--ink);
    overflow-wrap: anywhere;
}

/* A blockquote, but set as running copy rather than an indented pull
   quote — the card already frames it. */
.review__text {
    margin: 0;
    padding: 0;
    border: 0;
    max-width: none;
    color: var(--ink);
}

/* ---------- Gallery carousel -------------------------------- */

/* A gallery attached to a page, rendered below (or above) its content —
   see "Resources above & below content" in Hydra.

   The carousel is a scroll-snapping strip, not a JavaScript slideshow: the
   swiping works natively, so with scripting off it is still a usable
   gallery. client.js only adds arrows and a counter on top. */
.gallery {
    position: relative;
    clear: both;                /* never sit beside a floated image above */
    float: none;
    width: 100%;
    margin-block: var(--space-lg);
}

.gallery__track {
    display: flex;
    gap: var(--space-sm);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    list-style: none;
    margin: 0;
    padding: 0;
    /* Firefox / standards scrollbar, kept slim so it does not fight the
       arrows sitting over the strip. */
    scrollbar-width: thin;
    overscroll-behavior-x: contain;
}

.gallery__track:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
}

.gallery__slide {
    flex: 0 0 100%;
    scroll-snap-align: center;
    /* A fixed frame is what stops a portrait shot among landscape ones
       from changing the height of the whole carousel as you scroll. */
    aspect-ratio: 3 / 2;
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--deep);
}

.gallery__slide img {
    width: 100%;
    height: 100%;
    /* contain, not cover: these are mixed orientations (1500x1125 beside
       1500x2000) and cropping a portrait shot to 3:2 would throw away most
       of the picture. The dark frame behind it reads as intentional. */
    object-fit: contain;
}

/* ---------- Arrows + counter (added by client.js) ---------- */

.gallery__btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    display: grid;
    place-items: center;
    width: 44px;               /* a comfortable touch target */
    height: 44px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(18, 41, 30, 0.72);
    color: #FFFFFF;
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s ease, opacity 0.15s ease;
}

.gallery__btn:hover { background: rgba(18, 41, 30, 0.9); }

.gallery__btn--prev { left: var(--space-sm); }
.gallery__btn--next { right: var(--space-sm); }

/* At an end there is nowhere to go — say so rather than leaving a control
   that silently does nothing. */
.gallery__btn[disabled] {
    opacity: 0.35;
    cursor: default;
}

.gallery__count {
    max-width: none;
    margin: var(--space-xs) 0 0;
    text-align: center;
    font-size: var(--step--1);
    color: var(--ink-soft);
}

/* ---------- Services hero tiles ({{services-hero}}) ---------- */

/* Auto-fitting, so however many service pages exist the columns settle
   themselves — nothing to keep in step when a service is added. */
.services-hero {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
    gap: var(--space-md);
    list-style: none;
    margin: var(--space-lg) 0 0;
    padding: 0;

    /* Page copy above this may end with a floated .img_left / .img_right
       that runs taller than the text beside it. Without clearing, the grid
       would ride up into the gap alongside the float and lose its full
       width. Start below anything floated instead. */
    clear: both;
    float: none;
    width: 100%;
}

/* A fixed ratio is what makes the tiles a grid rather than a ragged wall:
   the banners behind them are all different shapes. The background shows
   through for a service with no image of its own. */
.service-tile__link {
    position: relative;
    display: block;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border-radius: var(--radius);
    background: var(--brand-dark);
    text-decoration: none;
}

.service-tile__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.35s ease;
}

.service-tile__link:hover .service-tile__img,
.service-tile__link:focus-visible .service-tile__img {
    transform: scale(1.05);
}

/* The title sits on the photograph, so it carries its own scrim rather
   than trusting whatever happens to be in the bottom of the image. */
.service-tile__title {
    position: absolute;
    inset: auto 0 0 0;
    padding: var(--space-md) var(--space-sm) var(--space-sm);
    background: linear-gradient(to top,
        rgba(18, 41, 30, 0.92) 0%,
        rgba(18, 41, 30, 0.65) 55%,
        rgba(18, 41, 30, 0) 100%);
    color: #FFFFFF;
    font-family: var(--font-display);
    font-size: calc(var(--h3-size) * var(--heading-scale));
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
}

/* No banner set, or the file has gone: the scrim would be invisible on the
   flat brand ground, so centre the title on the tile instead. */
.service-tile__link--plain .service-tile__title {
    inset: 0;
    display: grid;
    place-items: center;
    background: none;
}

.service-tile__link:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
}

/* ---------- Home page pair ({{reviews-pair}}) ---------- */

/* Two random reviews side by side. Reuses .review wholesale, so a change to
   the card shows up in both places. */
.reviews-pair {
    clear: both;               /* never sit beside a floated image above */
    margin-block: var(--space-lg);
    float: none;
    width: 100%;
}

/* Exactly two columns rather than the auto-fit the full grid uses — this is
   a fixed pair, not an open-ended list. */
.reviews-pair__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.reviews-pair__cta {
    max-width: none;
    margin-top: var(--space-md);
    margin-bottom: 0;
    text-align: center;
    font-weight: 700;
}

/* ---------- Clamped review text + [more] ---------- */

/* How many lines a card shows before it is cut off. One number, so the
   card height is tuned from here. */
.reviews__grid { --review-lines: 6; }

/* The clamp is applied by client.js, never by default — with scripting off
   every review stays fully readable rather than being cut off with no way
   to open it. */
.review.is-clamped .review__text {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: var(--review-lines);
    line-clamp: var(--review-lines);
    overflow: hidden;
}

/* Sits at the foot of the card, pushed there so the links line up across
   the row however short the review above them is. */
.review__expand {
    margin-top: auto;
    align-self: flex-start;
    padding: 0;
    border: 0;
    background: none;
    color: var(--link);
    font: inherit;
    font-weight: 700;
    font-size: var(--step--1);
    text-decoration: underline;
    cursor: pointer;
}

.review__expand::before { content: "["; }
.review__expand::after  { content: "]"; }

.review__expand:hover,
.review__expand:focus-visible { color: var(--link-hover); }

/* While a review is open, rows size to their own content instead. With
   1fr rows every row matches the tallest, so one expanded card would
   otherwise pad every other card in the grid with dead space. */
.reviews__grid.has-expanded { grid-auto-rows: auto; }

/* ---------- Show more ---------- */

/* Injected by client.js, and only when there are more reviews than one
   batch — so there is nothing to style when every review already fits. */
.reviews__more-wrap {
    max-width: none;
    margin-top: var(--space-lg);
    margin-bottom: 0;
    text-align: center;
}

/* A hidden card must stay hidden: .review is display:flex, which would
   otherwise win over the [hidden] attribute's display:none. */
.review[hidden] { display: none; }

/* The revealed batch takes focus for keyboard users — show that it has,
   without the ring implying the card itself is interactive. */
.review:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

/* ---------- Malformed-line notice (staging only) ---------- */

/* Amber, matching the duplicate-H1 warning, so both staging notices read
   as the same kind of thing. */
.reviews__notice {
    max-width: none;
    margin-top: var(--space-md);
    padding: var(--space-sm) var(--space-md);
    border-left: 4px solid #B45309;
    border-radius: var(--radius);
    background: #FEF3E2;
    color: #5C3A0B;
    font-size: var(--step--1);
}

/* ---------- Blog ------------------------------------------- */

/* Listing. Three to a line, fixed rather than auto-fitting: BLOG_PER_PAGE
   is 6, so three columns is exactly two full rows and a last page holding
   one or two articles still lines its cards up under the ones above.
   auto-fit would stretch a short last row to full width instead.
   Steps down to two columns on tablets and one on phones. */
.post-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-md);
    margin: var(--space-lg) 0 0;

    /* Page copy above may end with a floated image; start below it
       rather than riding up into the gap alongside. Same reason as
       .services-hero. */
    clear: both;
    width: 100%;
}

/* The card is the positioning context for the stretched link below. */
.post-card {
    position: relative;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: #FFFFFF;
    transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.post-card:hover,
.post-card:focus-within {
    transform: translateY(-3px);
    box-shadow: 0 10px 24px rgba(18, 41, 30, 0.14);
}

/* Fixed ratio: article thumbnails are whatever shape the photograph
   was, and a ragged row of cards reads as a mistake. */
.post-card__media {
    aspect-ratio: 16 / 10;
    overflow: hidden;
    background: var(--brand-light);
}

.post-card__media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.35s ease;
}

.post-card:hover .post-card__media img {
    transform: scale(1.04);
}

.post-card__body {
    display: flex;
    flex: 1;
    flex-direction: column;
    padding: var(--space-md);
}

.post-card__date {
    display: block;
    margin-bottom: var(--space-xs);
    color: var(--ink-soft);
    font-size: var(--step--1);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.post-card__title {
    margin: 0 0 var(--space-xs);
    font-size: var(--h3-size);
}

.post-card__title a {
    color: inherit;
    text-decoration: none;
}

/* One anchor, stretched over the whole card: the entire card is
   clickable, but there is still only one tab stop and one link for a
   screen reader to announce. */
.post-card__link::after {
    content: "";
    position: absolute;
    inset: 0;
}

.post-card__link:focus-visible {
    outline: none;
}

/* Focus has to show on the card, since the anchor itself is only the
   title text. */
.post-card:focus-within {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.post-card__excerpt {
    margin: 0 0 var(--space-sm);
    color: var(--ink-soft);
    font-size: var(--step-0);
}

/* Pushed to the bottom so it lines up across cards of unequal length. */
.post-card__more {
    margin-top: auto;
    color: var(--accent);
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.post-empty {
    margin: var(--space-lg) 0;
    color: var(--ink-soft);
}

.post-count {
    margin: var(--space-md) 0 0;
    color: var(--ink-soft);
    font-size: var(--step--1);
    text-align: center;
}

/* ---------- Pagination ---------- */

.pager {
    margin: var(--space-md) 0 var(--space-lg);
}

.pager__list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
    align-items: center;
    justify-content: center;
    margin: 0;
    padding: 0;
    list-style: none;
}

.pager__link {
    display: inline-block;
    min-width: 2.5rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    color: var(--link);
    font-size: var(--step--1);
    font-weight: 700;
    text-align: center;
    text-decoration: none;
    transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

a.pager__link:hover,
a.pager__link:focus-visible {
    border-color: var(--brand);
    background: var(--brand-light);
}

.pager__link.is-current {
    border-color: var(--brand);
    background: var(--brand);
    color: #FFFFFF;
}

/* Kept in the DOM rather than dropped, so the row does not jump
   sideways between page 1 and page 2. */
.pager__link.is-disabled {
    color: var(--ink-soft);
    opacity: 0.45;
}

/* ---------- Article page ---------- */

.post-head {
    margin-bottom: var(--space-md);
}

/* The back button, first thing on the article. Sized as a real target
   rather than a text link — it is the primary way back to the listing. */
.post-back {
    display: inline-flex;
    gap: 0.5rem;
    align-items: center;
    padding: 0.5rem 0.9rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    color: var(--link);
    font-size: var(--step--1);
    font-weight: 700;
    text-decoration: none;
    transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.post-back:hover,
.post-back:focus-visible {
    border-color: var(--brand);
    background: var(--brand-light);
}

.post-back:hover .post-back__arrow {
    transform: translateX(-2px);
}

.post-back__arrow {
    display: inline-block;
    transition: transform 0.2s ease;
}

.crumbs {
    margin: var(--space-sm) 0 0;
}

.crumbs__list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin: 0;
    padding: 0;
    list-style: none;
    color: var(--ink-soft);
    font-size: var(--step--1);
}

/* Separator drawn in CSS, so it is never read out as content. */
.crumbs__list li + li::before {
    content: "/";
    margin-right: 0.4rem;
    color: var(--ink-soft);
}

.crumbs__list a {
    color: var(--ink-soft);
}

.post-meta {
    margin: var(--space-sm) 0 0;
    color: var(--ink-soft);
    font-size: var(--step--1);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.post-title {
    margin-top: var(--space-xs);
}

/* Three 300px-ish cards plus gaps stop fitting somewhere around 1000px. */
@media (max-width: 1000px) {
    .post-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 768px) {
    .post-card__body { padding: var(--space-sm); }
    .pager__link     { min-width: 2.25rem; padding: 0.45rem 0.6rem; }
}

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

/* ---------- Mobile menu (burger + drop-down) --------------- */

/* Both are desktop-hidden and switched on in the 768px block below, so the
   desktop bar is untouched by any of this. */

.burger {
    display: none;
    align-items: center;
    justify-content: center;
    width: 44px;                 /* 44px is the smallest comfortable tap target */
    height: 44px;
    padding: 0;
    border: 0;
    background: none;
    color: currentColor;         /* follows the header's ink, plain bar included */
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.burger__bars {
    display: block;
    width: 26px;
}

.burger__bar {
    display: block;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
    /* Transformed about their own centres, so the two outer bars meet in the
       middle of the stack when they rotate. */
    transition: transform 0.28s ease, opacity 0.18s ease;
}

.burger__bar + .burger__bar { margin-top: 6px; }

/* Bars sit at 0, 8 and 16px; the centre line is 8px down, so the outer two
   travel 8px to meet it and cross. */
.site-header.is-menu-open .burger__bar:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.site-header.is-menu-open .burger__bar:nth-child(2) { opacity: 0; }
.site-header.is-menu-open .burger__bar:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* --- The drop-down --- */

/* Anchored to the fixed header, so it drops over the page instead of
   pushing it down. Closed by default: with no JavaScript the button does
   nothing and this stays shut, which is the same as the old behaviour
   (the desktop list was simply hidden on phones). */
.mobile-menu {
    display: none;
    position: absolute;
    inset: 100% 0 auto 0;
    max-height: 0;
    overflow: hidden;
    visibility: hidden;
    background: var(--deep);
    border-top: 1px solid var(--rule);
    box-shadow: 0 14px 28px rgba(0, 0, 0, 0.35);

    /* visibility flips only once the height animation has finished, so the
       links are never tabbable while the box is closing. */
    transition: max-height 0.32s ease, visibility 0s linear 0.32s;
}

.site-header.is-menu-open .mobile-menu {
    max-height: min(78vh, 640px);
    overflow-y: auto;
    visibility: visible;
    transition: max-height 0.32s ease, visibility 0s;
}

.mobile-menu__inner {
    padding-block: var(--space-sm) var(--space-md);
}

.mobile-menu__list,
.mobile-menu__sub {
    margin: 0;
    padding: 0;
    list-style: none;
}

.mobile-menu__link {
    display: block;
    padding: 0.85rem 0;
    border-bottom: 1px solid var(--rule);
    color: #FFFFFF;
    font-family: var(--font-body);
    font-size: var(--step-1);
    font-weight: 600;
    text-decoration: none;
}

.mobile-menu__link[aria-current="page"] {
    color: var(--accent);
}

/* A group is a label, not a link. Styled as a heading for the pages under
   it so nothing invites a tap that would do nothing. */
.mobile-menu__link--group {
    color: var(--ink-soft);
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    cursor: default;
}

/* Children of a group, e.g. the individual services. Indented and lighter
   rather than hidden behind a second tap. */
.mobile-menu__link--sub {
    padding-left: var(--space-md);
    font-size: var(--step-0);
    font-weight: 500;
    color: var(--ink-soft);
}

.mobile-menu__contact {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    margin-top: var(--space-md);
}

.mobile-menu__contact-link {
    color: var(--accent);
    font-weight: 700;
    text-decoration: none;
    word-break: break-word;      /* the email address is long */
}

/* ---------- 8. Mobile breakpoint (≤ 768px) ----------------- */

@media (max-width: 768px) {

    :root {
        --space-2xl: 3.5rem;
        --space-xl:  2.5rem;
        --space-lg:  1.75rem;
    }

    body { line-height: 1.6; }

    h1, h2 { letter-spacing: -0.01em; }


    .grid { gap: var(--space-md); }

    /* One card per row on phones — a 280px minimum would otherwise still
       allow two cramped columns on wider handsets. */
    .reviews__grid { grid-template-columns: 1fr; }
    .reviews-pair__grid { grid-template-columns: 1fr; }

    /* Arrows would cover too much of the picture on a phone; the strip
       still swipes natively. */
    .gallery__btn { display: none; }

    /* Portrait frame on phones, so the pictures fill the width.

       The slide is a fixed frame with object-fit: contain, which is what
       stops the carousel changing height as you scroll. At the desktop
       3:2 that costs nothing on a landscape shot — but every image in the
       gallery is portrait (1125x1500), and a 3:4 picture letterboxed into
       a 3:2 box renders at only half the slide width, with dark bands
       down both sides. Matching the frame to the pictures fills the width
       and keeps the fixed height.

       A landscape image added later still letterboxes rather than being
       cropped, which is the same trade the desktop frame already makes. */
    .gallery__slide { aspect-ratio: 3 / 4; }

    /* The panel keeps its ground but gives the cards back their width. */
    .reviews { padding: var(--space-md) var(--space-sm); }

    /* The reference site hides these below 768px; wrapping them smaller
       keeps the credentials visible on the devices most visitors use. */
    .companies img { height: 52px; }
    .companies { gap: var(--space-sm) var(--space-md); }

    /* Was 64px, a shorter bar than desktop. Kept at the desktop height
       instead so the enlarged logo has room — see .site-logo below. */
    :root { --header-h: 84px; }

    /* A 21:9 strip is too shallow for centred copy on a phone — go
       taller and let object-fit/cover crop the sides. */
    :root { --video-ratio: 95%; }

    /* Banner 35% deeper than the desktop 21:9 (42.857% x 1.35). The
       banner is a background sized with cover, so this crops the sides
       rather than stretching anything. */
    :root { --banner-ratio: 57.857%; }

    .video-hero__inner p { font-size: var(--step-0); }

    /* Two columns will not fit a phone — copy above, form below. */
    .page-split {
        grid-template-columns: 1fr;
        gap: var(--space-md);
    }

    /* Three columns will not fit a phone — stack them. */
    .site-footer__inner {
        grid-template-columns: 1fr;
        gap: var(--space-md);
    }
    :root { --hero-logo-h: 60.8px; }   /* 76px, less 20% */

    /* The desktop list would wrap the bar onto two lines; the burger and
       its drop-down replace it below this width. */
    .site-nav { display: none; }

    /* Bigger logo on phones — it was capped at 29px tall, against a 64px
       bar. --logo-h is a formula tuned for the desktop bar, so both caps
       are set outright here instead. The logo is 300x96, so the height cap
       is what binds: 56px tall, about 175px wide, inside an 84px bar. */
    .site-logo img {
        max-width: 200px;
        max-height: 56px;
    }

    /* Phone and email give way to the burger — the drop-down carries both,
       so nothing is actually lost. */
    .site-contact { display: none; }

    .burger      { display: inline-flex; }
    .mobile-menu { display: block; }

    /* The bar centres its two groups on a phone: with the menu gone from
       the top line there is nothing to align a baseline to. */
    .site-header__inner,
    .site-header__primary,
    .site-header__secondary { align-items: center; }

    /* Stops the page scrolling behind an open menu. */
    body.has-menu-open { overflow: hidden; }

    /* Full-width tap targets for primary actions */
    .btn { display: block; width: 100%; padding-block: 0.95em; }
    .btn + .btn { margin-top: var(--space-xs); }
}

/* ---------- 9. Motion / print ------------------------------ */

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

@media print {
    body { background: #FFFFFF; color: #000000; }
    .site-header {
        position: static;
        background: none;
        color: #000000;
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
    a { text-decoration: underline; }
    .section { padding-block: var(--space-md); }
}
