/* =================================================================================================
   harley.css - Harley's own theme. One file, self-contained, nothing borrowed at runtime.

   WHY THIS IS NOT A COPY OF HIS STYLESHEETS
   -----------------------------------------
   Craig: "Her own theme: her own banner, logo, favicon. She likes the look of Craig's site, so the
   same aesthetic, but her own theme file rather than sharing his."

   His look is produced by four stylesheets totalling about 360KB, most of it a bought theme
   (Bootstrap 3 plus Stack) that his pages then spend a great many lines fighting. Copying that
   stack onto her subdomain would have carried the fights across with it, and his own comments
   record what those fights are:

     - theme.css redefines `.bg--white` as #d2d2d2, a mid grey. Every "white" card on his site is
       not white, which is why a link colour that looked obviously fine measured 2.18:1.
     - theme.css sets `h2` to 2.36em AND centres it, so a real section heading comes out as a huge
       banner. That is why pages across the site went from h1 straight to h3 and had no h2 at all,
       which is exactly what an answer engine uses to decide what to quote.
     - `input[type=...]` selectors in the theme outrank any plain class, which once shipped an
       invisible search box, and `button:focus { outline: none !important }` removed the focus ring
       that keyboard users depend on.

   None of those traps are in this file, because the rules that cause them are not in this file. It
   is roughly 5% of the size, and every colour in it was chosen against the ground it actually
   sits on rather than an assumed white.

   CONTRAST
   --------
   Every pair below carries its measured ratio. The requirement is 4.5:1 for ordinary text, 3:1 for
   large text and for the boundary of a control. Ratios are computed against the REAL composited
   background, never an assumed one, because that assumption is what hid the worst failure on the
   parent site.

   ONE PLACE FOR EVERY COLOUR
   --------------------------
   His stylesheets have no variables, so changing the brand red means a find-and-replace across
   several files and any inline style anybody ever wrote. Hers are custom properties. Changing her
   accent is one line, which matters because whether she keeps his red is still an open question.
   ================================================================================================= */

/* =================================================================================================
   HER FONTS, SERVED FROM HER OWN SERVER
   -------------------------------------
   Until 24/08/2026 every page of this site opened with a stylesheet link to fonts.googleapis.com,
   which hands Google the IP address, the page address and the browser fingerprint of every visitor
   before a word has been drawn. inc/layout.php states, in as many words, that there is deliberately
   no jQuery, no theme bundle and no third-party tracking here, "because each of those is a decision
   with a privacy consequence for people whose visit here is their business alone". A font link to
   Google was exactly that decision, made by accident, and it contradicted the note sitting a few
   lines above it. The fonts are hers now, so the note is true.

   ⚠ THESE FILES ARE HERS AND MUST STAY HERS. harley/fonts/ is a separate copy from the parent
   site's fonts/ folder. This is a different website with its own document root and it may not load
   a file from his; a gate check fails the build if it ever does. The duplication is deliberate, so
   do not "fix" it.

   Only what this stylesheet actually asks for is here, because a self-hosted set carrying weights
   nobody uses is slower than the thing it replaced:

     Open Sans, upright   one variable file covering 300 to 700. The rules below use 300, 400, 600
                          and 700. There is no italic file: nothing on the public pages is in
                          italic Open Sans, which is why Google was never asked for one either.
     Merriweather 300     .ha-reviews-number, the big count on the testimonials panel.
     Merriweather 300 i   blockquote.

   Latin subset only, which covers everything these pages write, including the pound sign, accented
   vowels, curly quotes and the ellipsis. ⚠ The unicode-range lines must stay: without them a
   browser uses these files for every character and draws an empty box for anything they lack.

   Both families are under the SIL Open Font Licence 1.1, which permits self-hosting. The full texts
   are beside the fonts as fonts/open-sans-OFL.txt and fonts/merriweather-OFL.txt, copied from
   github.com/google/fonts. The woff2 files are the same ones Google was serving, fetched from
   fonts.gstatic.com on 24/08/2026, so nothing about her site's appearance changes.

   font-display: swap on every face, so the words are readable in a fallback immediately and are
   never invisible while a font loads.
   ================================================================================================= */
@font-face {
    font-family: 'Open Sans';
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url('../fonts/open-sans-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
                   U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
                   U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Merriweather';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src: url('../fonts/merriweather-latin-300.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
                   U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
                   U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Merriweather';
    font-style: italic;
    font-weight: 300;
    font-display: swap;
    src: url('../fonts/merriweather-latin-300-italic.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
                   U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
                   U+FEFF, U+FFFD;
}

:root {
    /* --- Grounds ------------------------------------------------------------------------------
       "Masquerade Noir", picked by Harley on 21/08/2026 out of five directions she was shown.
       The ground moved from his gunmetal (#1b1d20) to near-black with a faint violet bias, lit by
       a single red spotlight from above - one light is the whole idea of the direction.

       ⚠ --ha-bar IS NOW LIGHTER THAN --ha-page, WHICH IS THE OPPOSITE OF HOW IT STARTED, and it
       has to be. It was the DARKER strip against gunmetal. Against near-black there is no darker
       left, so keeping it dark made the phone and tagline pills disappear into the page. It is a
       RAISED surface now. If the ground is ever repainted again, check which way round these two
       go rather than assuming: the name says "bar", not "darker".

       ⚠ THE CARD COLOURS DID NOT CHANGE AND MUST NOT. Craig, 21/08/2026: "Please keep the
       container colours, but the background behind that can be changed." Every ratio measured on
       --ha-card below is therefore untouched by the repaint, which is the point of the cards being
       their own fixed surface rather than a tint of the page.
       ------------------------------------------------------------------------------------------ */
    --ha-page:        #0a090c;   /* near-black, violet-biased. The ground the spotlight falls on  */
    --ha-bar:         #17151b;   /* RAISED strip behind the phone and tagline pills. See above    */
    --ha-card:        #e6e6e6;   /* content card. A REAL value, chosen, not inherited            */
    /* ⚠ PALE GREY, NOT WHITE, AND A DARKER INK TO PAY FOR IT. Craig, 22/08/2026: "the white in the
       editable fields is a little harsh on the eyes. Could it be a pale grey, please? And make the
       text a little darker to make up for that."
       That instinct is right and it is worth naming: a white box on a near-black page is the
       brightest thing on the screen, and her admin is where somebody sits for half an hour at a
       time. Softening the ground on its own would have COST contrast, which is why the ink moves
       with it: #101012 on #e9e9ea measures 15.67:1, against 16.05:1 before. Softer to look at and
       no worse to read. */
    --ha-input:       #e9e9ea;   /* form field ground. Pale grey, not white                      */
    --ha-input-ink:   #101012;   /* what you type.              15.67:1 on --ha-input            */

    /* --- Ink ---------------------------------------------------------------------------------- */
    --ha-ink:         #1c1c1c;   /* body text on a card.        13.65:1 on --ha-card             */
    --ha-ink-muted:   #4a4a4a;   /* help text, small print.      7.10:1 on --ha-card             */
    --ha-on-dark:     #ffffff;   /* text on the page ground.    19.86:1 on --ha-page             */
    --ha-on-dark-2:   #cccccc;   /* secondary text on dark.     12.37:1 on --ha-page             */

    /* --- Accent -------------------------------------------------------------------------------
       TWO reds, and the reason is arithmetic rather than taste.

       #b42828 is his brand red. On a light card it measures 5.14:1 and is fine for text.
       On the dark page ground it measures 3.05:1, which fails text (4.5:1) even now the ground is
       near-black. His own stylesheet records hitting this exact wall at 2.64:1.

       So the dark surfaces get a lifted red, #e05c5c, at 5.54:1 on --ha-page. Same family, same
       impression, and it is legible. Using one red everywhere would have meant either an
       unreadable dark UI or a washed-out light one.

       The noir repaint IMPROVED both of these, because the ground got darker. That is the safe
       direction, but it is not a reason to stop measuring: darkening a ground helps light text and
       hurts nothing only while the text stays light. tests/harley-contrast.php now recomputes every
       pair from this file on each deploy, so the next repaint cannot quietly break one.
       ----------------------------------------------------------------------------------------- */
    --ha-accent:      #b42828;   /* on light cards.  5.14:1 on --ha-card                          */
    --ha-accent-dark: #e05c5c;   /* on dark grounds. 5.54:1 on --ha-page                          */
    --ha-accent-soft: rgba(180, 40, 40, 0.55);   /* decorative underlines on light                */
    --ha-accent-edge: rgba(224, 92, 92, 0.75);   /* control borders on dark                       */

    /* --- Links (on a light card) ---------------------------------------------------------------
       Underlined as well as coloured, and that is a requirement rather than a style choice. A link
       identified by colour alone needs 3:1 against the surrounding text; #1a5490 against the body
       ink measures well under that, so colour alone could not mark it. The underline carries it.  */
    --ha-link:        #1a5490;   /* 6.20:1 on --ha-card                                           */
    --ha-link-hover:  #0f3a68;   /* 9.22:1 on --ha-card                                           */

    /* --- Availability traffic light -----------------------------------------------------------
       Measured on --ha-card, where the pills actually sit. The obvious greens and ambers all came
       in between 3 and 4.4:1 there and were darkened until they passed.                          */
    --ha-green:       #1a6b2e;   /* 5.28:1 on --ha-card                                           */
    --ha-orange:      #8a5a00;   /* 4.75:1 on --ha-card                                           */
    --ha-red:         #a3231b;   /* 5.98:1 on --ha-card                                           */

    /* --- Alerts ------------------------------------------------------------------------------- */
    --ha-ok-ink:      #1d5c1d;   --ha-ok-bg:   #eefaee;   --ha-ok-edge:   rgba(30, 110, 30, 0.45);
    --ha-bad-ink:     #8a1a1a;   --ha-bad-bg:  #fdf0f0;   --ha-bad-edge:  rgba(180, 40, 40, 0.45);
    --ha-warn-ink:    #7a4e00;   --ha-warn-bg: #fff8e9;   --ha-warn-edge: rgba(160, 110, 0, 0.45);

    --ha-radius:      10px;
    --ha-maxw:        1120px;
}

/* -------------------------------------------------------------------------------------------------
   RESET, kept to the few rules that actually matter rather than a full reset nobody reads.
   ------------------------------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
    font-size: 16px;
    line-height: 1.65;
    color: var(--ha-on-dark);

    /* The ground: near-black, with one red spotlight thrown from above. It is on BODY, not on an
       inner wrapper, and that is load-bearing: a background only ever paints inside its own
       element's box, so putting it on a middle section leaves the header and footer showing a flat
       colour and a visible join. His site shipped exactly that fault before the rule moved here.

       The light is a WIDE, LOW-OPACITY radial rather than a tight bright one. A spotlight strong
       enough to admire is strong enough to drop the contrast under it, and every value in :root is
       measured against the flat --ha-page. At these opacities the composited ground stays within a
       hair of it; anything stronger would make those measurements a work of fiction.

       `fixed` so the light stays put while the page scrolls, which is what a light in a room does.
       It is dropped on a phone further down: iOS repaints a fixed background on every scroll frame
       and it stutters. */
    background-color: var(--ha-page);
    background-image:
        radial-gradient(120% 62% at 50% -6%, rgba(180,40,40,.30), rgba(180,40,40,.07) 42%, transparent 68%),
        radial-gradient(80% 46% at 50% 104%, rgba(122,26,26,.20), transparent 62%),
        repeating-linear-gradient(112deg, rgba(255,255,255,.012) 0 1px, transparent 1px 5px);
    background-attachment: fixed;

    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

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

/* -------------------------------------------------------------------------------------------------
   HEADINGS

   h2 is styled here as a SECTION HEADING and nothing else: about 1.3x the body text, left aligned,
   more space above than below so it binds to the text it introduces.

   That is a deliberate correction of the trap his site fell into. When a theme renders h2 as a huge
   centred page title, whoever writes the next page quietly reaches for h3 because it "looks right",
   and the document loses its section boundaries to save a font size. Every page on his site went h1
   straight to h3 for exactly that reason. An answer engine cuts a page into quotable passages at
   its h2 boundaries, so a page with none offers nowhere to cut.
   ------------------------------------------------------------------------------------------------- */
h1, h2, h3, h4 {
    font-family: "Open Sans", Arial, sans-serif;
    color: var(--ha-ink);
    line-height: 1.3;
    font-weight: 600;
}
h1 { font-size: 2rem;    margin: 0 0 0.5em;        letter-spacing: -0.01em; }
h2 { font-size: 1.4rem;  margin: 1.9em 0 0.55em;   text-align: left; }
h3 { font-size: 1.08rem; margin: 1.5em 0 0.4em; }
h4 { font-size: 0.95rem; margin: 1.2em 0 0.35em; }
h2:first-child, h3:first-child { margin-top: 0; }

p  { margin: 0 0 1.05em; }
ul, ol { margin: 0 0 1.05em; padding-left: 1.3em; }
li { margin-bottom: 0.35em; }
hr { border: 0; border-top: 1px solid rgba(0, 0, 0, 0.14); margin: 2em 0; }

blockquote {
    font-family: Merriweather, Georgia, serif;
    font-style: italic;
    font-size: 0.97em;
    margin: 1.4em 0;
    padding: 0.2em 0 0.2em 1.1em;
    border-left: 3px solid var(--ha-accent-soft);
    color: var(--ha-ink);
}

/* -------------------------------------------------------------------------------------------------
   LAYOUT
   ------------------------------------------------------------------------------------------------- */
.ha-container { width: 100%; max-width: var(--ha-maxw); margin: 0 auto; padding: 0 18px; }
.ha-main { flex: 1 0 auto; padding-bottom: 2.2em; }

.ha-row { display: flex; flex-wrap: wrap; gap: 22px; align-items: flex-start; }
.ha-col-main { flex: 1 1 620px; min-width: 0; }
.ha-col-side { flex: 0 1 300px; min-width: 0; }

/* The light content card. This IS the reading surface, so it is a real value chosen against the
   ink that sits on it, not a class called "white" that resolves to grey. */
.ha-card {
    background: var(--ha-card);
    color: var(--ha-ink);
    border: 1px solid rgba(180, 40, 40, 0.45);
    border-radius: var(--ha-radius);
    padding: 26px 28px;
    margin-bottom: 22px;
}
.ha-card:last-child { margin-bottom: 0; }

/* Links inside a card. Colour AND underline, for the reason noted beside --ha-link. */
.ha-card a:not([class]) {
    color: var(--ha-link);
    text-decoration: underline;
    text-decoration-color: var(--ha-accent-soft);
    text-underline-offset: 3px;
}
.ha-card a:not([class]):hover,
.ha-card a:not([class]):focus {
    color: var(--ha-link-hover);
    text-decoration-color: var(--ha-accent);
}

/* -------------------------------------------------------------------------------------------------
   SKIP LINK

   The colour is restated for :focus on purpose. Focus is the ONLY moment this link is on screen, so
   any stylesheet rule that targets a:focus would otherwise win at exactly the moment it matters.
   That is not hypothetical: on the parent site a Bootstrap a:focus rule painted this link dark blue
   on its own near-black ground at 2.25:1, and it was only found by tabbing to it in a browser.
   ------------------------------------------------------------------------------------------------- */
.ha-skip {
    position: absolute; left: -9999px; top: 0; z-index: 2000;
    background: #141414; color: #ffffff;          /* 18.42:1 */
    padding: 10px 18px; border-radius: 0 0 6px 0; text-decoration: none;
}
.ha-skip:focus, .ha-skip:hover, .ha-skip:active {
    left: 0; color: #ffffff; outline: 2px solid #ffffff; outline-offset: 2px;
}

/* -------------------------------------------------------------------------------------------------
   NAVIGATION
   ------------------------------------------------------------------------------------------------- */
.ha-nav { padding: 14px 0 6px; }
.ha-nav-inner { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 26px; }
.ha-brand { display: flex; align-items: center; gap: 12px; text-decoration: none; }
.ha-brand img { height: 54px; width: auto; }
.ha-brand-name {
    font-size: 1.3rem; font-weight: 700; letter-spacing: 0.04em;
    color: var(--ha-on-dark); text-transform: uppercase;
}

.ha-menu { list-style: none; display: flex; flex-wrap: wrap; gap: 4px 18px; margin: 0 0 0 auto; padding: 0; }
.ha-menu li { margin: 0; }
.ha-menu a {
    color: var(--ha-on-dark);                      /* 16.89:1 on --ha-page */
    text-decoration: underline;
    text-decoration-color: var(--ha-accent-edge);
    text-underline-offset: 4px;
    font-size: 0.9rem; font-weight: 600; letter-spacing: 0.02em;
    padding: 4px 0; display: inline-block;
}
.ha-menu a:hover, .ha-menu a:focus { color: var(--ha-accent-dark); text-decoration-color: var(--ha-accent-dark); }
.ha-menu a[aria-current="page"] { color: var(--ha-accent-dark); }

/* -------------------------------------------------------------------------------------------------
   HERO, TAGLINE AND CONTACT BARS
   ------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------
   THE BANNER

   ⚠ NOTHING MAY BE PLACED OVER THIS. Craig relayed Harley's decision on 21/08/2026: his own site
   puts the nav ON the banner inside a translucent box, and she asked for the opposite - the banner
   "exactly as it does in the example with nothing over the top of it", with her nav underneath. So
   the nav sits BELOW it in inc/layout.php, and this rule has no ::after tint over the image any
   more. The darkening it used to do is drawn into img/banner.svg instead, as a vignette, which
   gets the same result without a layer on top.

   If something later needs to appear on the banner, it goes INSIDE banner.svg. It does not go here.
   ------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------
   THE RIBBON, AS THE PAGE'S GROUND

   ⚠ THIS REPLACED A 140px BANNER AT THE TOP OF EVERY PAGE (22/08/2026). Craig: "any time a visitor
   arrives at any page of the website, almost their entire first screen is taken up with banner and
   no content is visible. The ribbon effect is really nice, but it cannot be forcing users to scroll
   past it on every page load." He was shown three ways to keep the ribbon and chose this one.

   Fixed, not scrolling, and behind everything. Fixed matters: a background that scrolls with a long
   page runs out of artwork and leaves plain black below the fold, which is exactly the problem being
   solved wearing different clothes.

   ⚠ IT IS A SEPARATE FILE FROM banner.svg AND MUST STAY ONE. The banner is drawn 1600x420 - wide and
   shallow, ribbons in the middle third - and stretched down a page it becomes four flat lines at the
   top and empty dark everywhere else. ribbon-bg.svg is drawn tall for this job.

   ⚠ AND IT IS FAINT ON PURPOSE. The banner could be bold because nothing was ever on top of it. This
   sits behind every card, paragraph and form on the site, so every opacity in the artwork is roughly
   half the banner's. If it is ever brightened, check the body text over it first - the cards are
   opaque, but the tagline, the contact bar and the footer are not.
   ------------------------------------------------------------------------------------------------- */
.ha-ribbonbg {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background: var(--ha-page) url("../img/ribbon-bg.svg") top center / cover no-repeat;
}
/* Everything the page draws sits above it. The value is 1 rather than something larger on purpose:
   the nav bar, the modals and the skip link already have their own stacking and must stay on top. */
.ha-main, .ha-nav, .ha-tagline, .ha-contactbar, .ha-footer, .ha-skip { position: relative; z-index: 1; }

/* The old banner element. Kept as a no-op so a page that still asks for one cannot leave a hole. */
.ha-hero, .ha-hero-wrap { display: none; }

.ha-tagline { text-align: center; padding: 14px 0 4px; }
.ha-tagline p {
    display: inline-block; margin: 0;
    /* WHITE, not the secondary grey. Craig, 22/08/2026: "please make this white. All other text on
       the site with a dark background is." It was the odd one out, and a line sitting directly
       under the banner is not secondary text. 18.11:1 on --ha-bar, up from 10.20:1. */
    background: var(--ha-bar); color: var(--ha-on-dark);
    border: 1px solid var(--ha-accent-edge); border-radius: var(--ha-radius);
    padding: 5px 24px; font-size: 1.08rem;
}

.ha-contactbar { display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; padding: 4px 0 14px; }
.ha-contactbar a {
    background: var(--ha-bar); color: var(--ha-on-dark);      /* 16.4:1 on --ha-bar */
    border: 1px solid var(--ha-accent-edge); border-radius: var(--ha-radius);
    padding: 5px 24px; font-weight: 600; letter-spacing: 0.04em;
    text-decoration: underline; text-decoration-color: var(--ha-accent-edge); text-underline-offset: 3px;
}
.ha-contactbar a:hover, .ha-contactbar a:focus { color: var(--ha-accent-dark); text-decoration-color: var(--ha-accent-dark); }

/* -------------------------------------------------------------------------------------------------
   BUTTONS
   ------------------------------------------------------------------------------------------------- */
.ha-btn {
    display: inline-block;
    background: #111111; color: #ffffff;           /* 18.88:1 */
    border: 1px solid var(--ha-accent-edge); border-radius: var(--ha-radius);
    padding: 7px 26px;
    font: inherit; font-size: 0.86rem; font-weight: 600;
    letter-spacing: 0.06em; text-transform: uppercase;
    text-decoration: underline; text-decoration-color: var(--ha-accent-edge); text-underline-offset: 3px;
    cursor: pointer;
}
.ha-btn:hover, .ha-btn:focus { color: var(--ha-accent-dark); text-decoration-color: var(--ha-accent-dark); }

/* A real outline, and it is !important on purpose. Bought themes very commonly carry
   `button:focus { outline: none !important }`, and an important declaration can only be beaten by
   another one. Removing a focus ring leaves keyboard users with no way to tell where they are.
   An outline is also not clipped by an ancestor's overflow:hidden, which is what hid the focus
   state entirely inside the parent site's booking accordions. */
.ha-btn:focus-visible,
.ha-card a:focus-visible,
.ha-menu a:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible {
    outline: 3px solid #ffffff !important;          /* 16.89:1 on the page ground */
    outline-offset: 2px;
}
.ha-card .ha-btn:focus-visible,
.ha-card input:focus-visible,
.ha-card select:focus-visible,
.ha-card textarea:focus-visible {
    outline-color: #111111 !important;              /* 13.10:1 on --ha-card */
}

.ha-btn-primary { background: var(--ha-accent); border-color: #7d1c1c; color: #ffffff; }  /* 6.62:1 */
.ha-btn-primary:hover, .ha-btn-primary:focus { background: #8f2020; color: #ffffff; }     /* 8.36:1 */

.ha-btnrow { display: flex; flex-wrap: wrap; gap: 10px; margin: 1.2em 0 0; }

/* -------------------------------------------------------------------------------------------------
   FORMS
   ------------------------------------------------------------------------------------------------- */
.ha-form .ha-field { margin-bottom: 1.9em; }
.ha-form label {
    display: block; font-weight: 600; font-size: 0.92rem;
    color: var(--ha-ink); margin-bottom: 0.35em;
}
.ha-form .ha-help {
    display: block; font-size: 0.82rem; color: var(--ha-ink-muted);   /* 7.10:1 */
    font-weight: 400; margin-top: 0.3em;
}
.ha-form .ha-required { color: var(--ha-accent); }                    /* 5.14:1 */

.ha-input {
    width: 100%; max-width: 460px;
    background: var(--ha-input); color: var(--ha-input-ink);          /* 15.67:1 */
    border: 1px solid var(--ha-accent);                               /* 6.04:1 boundary */
    border-radius: 6px;
    padding: 9px 12px;
    font: inherit; font-size: 0.95rem;
}
/* ⚠ NOT WHITE ON FOCUS EITHER. It was, and that would have put the harsh box straight back the
   moment she clicked into a field - on the one field she is actually looking at. Lifted a little
   so focus is still visible, and the border does most of that work anyway. */
.ha-input:focus { background: #f2f2f3; border-color: #7d1c1c; }
textarea.ha-input { min-height: 110px; resize: vertical; max-width: 100%; }
select.ha-input { max-width: 460px; }
.ha-input-sm { max-width: 220px; }
.ha-input-md { max-width: 320px; }

/* Placeholder text. Firefox dims placeholders by default, so opacity is reset or the measured
   ratio is not the ratio anybody actually sees. */
.ha-input::placeholder { color: #5c5c5c; opacity: 1; }                /* 5.90:1 on --ha-input */

/* --- The activities grid ---------------------------------------------------------------------- */
.ha-checkgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px 16px; margin-top: 0.5em; }
.ha-checkcat {
    grid-column: 1 / -1;
    font-size: 0.76rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em;
    color: var(--ha-accent);                                          /* 5.14:1 */
    border-bottom: 1px solid rgba(180, 40, 40, 0.28);
    margin: 1.1em 0 0.35em; padding-bottom: 3px;
}
.ha-checkcat:first-child { margin-top: 0; }
.ha-check {
    display: flex; flex-direction: row; align-items: flex-start; gap: 7px;
    font-weight: 400; font-size: 0.89rem; margin: 0; cursor: pointer;
}
.ha-check:hover { color: var(--ha-accent); }
.ha-check input { width: 14px; height: 14px; margin: 4px 0 0; accent-color: var(--ha-accent); flex: none; }
.ha-check span { line-height: 1.45; }

/* A tick box that stands alone (consent, age, flexible-on-time) rather than sitting in the grid. */
.ha-tick { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 1em; }
.ha-tick input { width: 18px; height: 18px; margin: 3px 0 0; accent-color: var(--ha-accent); flex: none; }
.ha-tick label { margin: 0; font-weight: 400; font-size: 0.92rem; }

/* The honeypot. Off-screen rather than display:none, because some automated fillers skip anything
   that is display:none and the whole point is that they should fill it in. aria-hidden and
   tabindex="-1" in the markup keep it away from people. */
.ha-trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* -------------------------------------------------------------------------------------------------
   ALERTS
   ------------------------------------------------------------------------------------------------- */
.ha-alert { border-radius: var(--ha-radius); padding: 13px 20px; margin-bottom: 1.4em; font-size: 0.94rem; border: 1px solid; }
.ha-alert p:last-child { margin-bottom: 0; }
.ha-alert-ok   { background: var(--ha-ok-bg);   color: var(--ha-ok-ink);   border-color: var(--ha-ok-edge); }   /*  7.26:1 */
.ha-alert-bad  { background: var(--ha-bad-bg);  color: var(--ha-bad-ink);  border-color: var(--ha-bad-edge); }  /*  8.11:1 */
.ha-alert-warn { background: var(--ha-warn-bg); color: var(--ha-warn-ink); border-color: var(--ha-warn-edge); } /*  7.03:1 */

/* -------------------------------------------------------------------------------------------------
   AVAILABILITY TRAFFIC LIGHT

   COLOUR IS NEVER THE ONLY CUE. Each state carries a symbol as well as a colour and a word, because
   red and green are the commonest pair to be unable to tell apart, and because a colour alone says
   nothing at all to a screen reader. The parent site's checker does the same; hers must not be the
   version that quietly drops it.
   ------------------------------------------------------------------------------------------------- */
.ha-avail { margin-top: 0.8em; }
.ha-slots { display: flex; flex-wrap: wrap; gap: 6px; margin: 0.7em 0; padding: 0; list-style: none; }
.ha-slot {
    display: inline-flex; align-items: center; gap: 5px;
    border: 1px solid currentColor; border-radius: 999px;
    padding: 3px 12px; font-size: 0.83rem; font-weight: 600;
    background: #ffffff; margin: 0;
}
.ha-slot .ha-sym { font-weight: 700; }
.ha-green  { color: var(--ha-green);  }   /* 5.28:1 on card, 6.15:1 on the pill's white ground */
.ha-orange { color: var(--ha-orange); }   /* 4.75:1 on card, 5.53:1 on white                   */
.ha-red    { color: var(--ha-red);    }   /* 5.98:1 on card, 6.96:1 on white                   */
.ha-unknown{ color: var(--ha-ink-muted); }

.ha-legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 0.82rem; margin-top: 0.6em; }
.ha-legend span { display: inline-flex; align-items: center; gap: 5px; }

/* Present to a screen reader, absent from the screen.
   NOT display:none and NOT visibility:hidden, because both of those remove the text from the
   accessibility tree as well, which would defeat the entire purpose. This is the clip-rect idiom,
   which keeps the text readable to assistive technology while occupying no visible space.
   It is what carries the word "seems free" alongside each coloured pill. */
.ha-sr {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    clip-path: inset(50%);
    white-space: nowrap; border: 0;
}

/* -------------------------------------------------------------------------------------------------
   SMALL PIECES
   ------------------------------------------------------------------------------------------------- */
.ha-lede { font-size: 1.06rem; }
.ha-small { font-size: 0.83rem; color: var(--ha-ink-muted); }         /* 7.10:1 */
.ha-eyebrow {
    font-size: 0.74rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.11em;
    color: var(--ha-accent); margin: 0 0 0.5em;                       /* 5.14:1 */
}
.ha-updated { font-size: 0.8rem; color: var(--ha-ink-muted); margin-top: 2em; }

/* A panel nested inside a card. Solid, never a translucent wash: a nested panel painted with an
   alpha over a light card composites to a muddy mid grey, and on the parent site that produced the
   worst link contrast anywhere on it at 1.16:1, invisible unless you composite the alpha by hand. */
.ha-panel {
    background: #f2f2f2; border: 1px solid rgba(0, 0, 0, 0.16);
    border-radius: 8px; padding: 16px 18px; margin-bottom: 16px;
}
.ha-panel:last-child { margin-bottom: 0; }

.ha-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; }

.ha-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.ha-table th, .ha-table td { text-align: left; padding: 8px 12px; border-bottom: 1px solid rgba(0,0,0,0.13); }
.ha-table th { font-weight: 700; }
.ha-tablewrap { overflow-x: auto; }

.ha-arrow { color: var(--ha-accent); font-weight: 700; }              /* 5.14:1 */
.ha-linklist { list-style: none; padding-left: 0; }
.ha-linklist li { margin-bottom: 0.55em; }

/* -------------------------------------------------------------------------------------------------
   FOOTER
   ------------------------------------------------------------------------------------------------- */
.ha-footer { flex: none; text-align: center; padding: 20px 0 26px; }
.ha-footer-links { display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.ha-footer-links a {
    background: var(--ha-bar); color: var(--ha-on-dark);              /* 16.4:1 on --ha-bar */
    border: 1px solid var(--ha-accent-edge); border-radius: var(--ha-radius);
    padding: 5px 22px; font-size: 0.78rem; font-weight: 600;
    letter-spacing: 0.06em; text-transform: uppercase;
    text-decoration: underline; text-decoration-color: var(--ha-accent-edge); text-underline-offset: 3px;
}
.ha-footer-links a:hover, .ha-footer-links a:focus { color: var(--ha-accent-dark); text-decoration-color: var(--ha-accent-dark); }
.ha-footer-note { font-size: 0.78rem; color: var(--ha-on-dark-2); margin: 0; }   /* 10.52:1 */
.ha-footer-note a { color: var(--ha-on-dark-2); text-decoration: underline; text-underline-offset: 3px; }

/* -------------------------------------------------------------------------------------------------
   RESPONSIVE
   ------------------------------------------------------------------------------------------------- */
@media (min-width: 768px) {
    body { font-size: 16.5px; }
    h1 { font-size: 2.35rem; }
    h2 { font-size: 1.6rem; }
    .ha-checkgrid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1200px) {
    .ha-checkgrid { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 767px) {
    .ha-card { padding: 20px 18px; }

    /* ⚠ THE NAVIGATION IS CENTRED ON A PHONE, on Craig's instruction (22/08/2026). On a wide screen
       the links sit hard right, balancing the logo. Stacked underneath it on a phone there is
       nothing to balance against, and left-aligned they read as a list that has fallen over.
       Centring needs all three of these: the column has to stop pinning its children left, the list
       has to centre the links that wrap onto a second row, and the auto margin that shoves it right
       has to go. */
    .ha-nav-inner { flex-direction: column; align-items: center; }
    .ha-menu { margin-left: 0; justify-content: center; }


    /* ⚠ EVERY CARD GOES FULL WIDTH. Craig: "all the containers full width. On her home page at the
       moment, testimonials and at a glance have less width."
       He was right, and this is the cause: the side column is `flex: 0 1 300px`, so when the row
       wraps on a phone it carries on asking for 300px, while the main column - `flex: 1 1 620px` -
       grows to fill the screen. The page ends up full width with two narrow cards stranded at the
       bottom, and on the home page those two are the testimonials and "at a glance".
       ⚠ flex-basis, not width. The basis IS where the 300px request comes from, so setting anything
       else would leave the request in place and merely argue with it. */
    .ha-col-side,
    .ha-col-main { flex-basis: 100%; }

    /* And the quotes stop trying to sit two abreast once the card is the width of a phone. */
    .ha-quotes { grid-template-columns: 1fr; }
}

/* Respect a stated preference for less movement. Nothing here animates much, but a hover that
   slides is still movement to somebody who has asked for none. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* A print of a page should be readable on paper: dark ink on white, no dark ground burning through
   a cartridge, and the navigation left off because a menu is meaningless printed. */
@media print {
    body { background: #ffffff; color: #000000; }
    .ha-nav, .ha-contactbar, .ha-hero, .ha-footer-links { display: none; }
    .ha-card { border: 1px solid #999999; background: #ffffff; }
}

/* -------------------------------------------------------------------------------------------------
   FEEDBACK / REVIEWS
   Seventy-odd quotes one after another is a wall of italics nobody reads to the bottom of, so each
   one sits in its own block with the handle and date under it. The italic serif of the base
   blockquote is kept for one or two in running text and dropped here: at this length it is harder
   to read, and these are somebody's plain words rather than a pull quote.
   ------------------------------------------------------------------------------------------------- */
.ha-quotes { display: grid; gap: 14px; }
.ha-quote {
    font-family: inherit;
    font-style: normal;
    font-size: 0.95em;
    margin: 0;
    padding: 13px 16px;
    border-left: 3px solid var(--ha-accent-soft);
    background: rgba(0, 0, 0, 0.03);
    border-radius: 0 var(--ha-radius) var(--ha-radius) 0;
}
.ha-quote p { margin: 0 0 0.5em; }
.ha-quote footer { margin: 0; }
@media (min-width: 900px) { .ha-quotes { grid-template-columns: 1fr 1fr; } }

/* The side-column testimonials panel, matching his site: a big count, then the quotes. The
   column itself is .ha-col-side, which already sits right on a wide screen and drops to the
   bottom on a narrow one, so nothing here positions anything. */
.ha-reviews-count { text-align: center; margin: 0 0 14px; padding-bottom: 10px;
                    border-bottom: 2px solid var(--ha-accent-soft); }
.ha-reviews-number { display: block; font-family: Merriweather, Georgia, serif; font-weight: 300;
                     font-size: 2.7em; line-height: 1; color: var(--ha-accent); }
.ha-reviews-label  { display: block; font-size: 0.9em; font-weight: 600; letter-spacing: 0.5px;
                     text-transform: uppercase; margin-top: 5px; }
.ha-reviews-widget h2 { font-size: 1.15rem; margin-top: 0; }
.ha-review { font-family: inherit; font-style: normal; font-size: 0.88em; margin: 0 0 14px;
             padding: 0 0 12px; border-left: 0; border-bottom: 1px solid rgba(0,0,0,0.10); }
.ha-review:last-of-type { border-bottom: 0; margin-bottom: 0; }
.ha-review footer { margin-top: 6px; font-size: 0.82em; color: var(--ha-ink-muted); }

/* -------------------------------------------------------------------------------------------------
   ADMIN NAVIGATION CARDS
   Built to match his members admin, at Craig's request (22/08/2026): an emoji, the name of the
   screen, and a line saying what it is for. The line is the point. "My details" and "Words on my
   pages" both sound like they might hold the same thing, and somebody unsure which to open opens
   neither.
   It is still a <nav> and still a <ul>: cards are a look, not a structure.
   ------------------------------------------------------------------------------------------------- */
.ha-admincards {
    list-style: none; margin: 0; padding: 0;
    display: grid; gap: 12px;
    grid-template-columns: repeat(4, minmax(0, 1fr));
}
.ha-admincards li { margin: 0; }

.ha-admincard {
    position: relative; display: block; height: 100%;
    background: rgba(0, 0, 0, 0.03);
    border: 1px solid var(--ha-accent-edge);
    border-radius: var(--ha-radius);
    padding: 16px 16px 14px;
    text-decoration: none;
    color: var(--ha-ink);
    transition: background 0.15s, border-color 0.15s, transform 0.12s;
}
.ha-admincard:hover, .ha-admincard:focus {
    background: rgba(180, 40, 40, 0.06);
    border-color: var(--ha-accent);
    transform: translateY(-2px);
    text-decoration: none;
}
/* The screen you are on. Marked with aria-current as well, so it is announced and not only shaded. */
.ha-admincard-here { background: rgba(180, 40, 40, 0.10); border-color: var(--ha-accent); }

.ha-admincard-icon  { display: block; font-size: 1.5em; line-height: 1; margin-bottom: 7px; }
.ha-admincard-title { display: block; font-weight: 700; font-size: 0.97em; margin-bottom: 3px; }
.ha-admincard-desc  { display: block; font-size: 0.8em; line-height: 1.45; color: var(--ha-ink-muted); }

/* Only ever a count of what is WAITING for her. A badge that counts rows teaches people to
   ignore badges. */
.ha-admincard-badge {
    position: absolute; top: 12px; right: 12px;
    background: var(--ha-accent); color: #ffffff;
    border-radius: 11px; padding: 1px 8px;
    font-size: 0.75em; font-weight: 700; line-height: 1.6;
}

/* Present for a screen reader, absent for everybody else. */
.ha-visually-hidden {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (max-width: 1100px) { .ha-admincards { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 820px)  { .ha-admincards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 480px)  { .ha-admincards { grid-template-columns: 1fr; } }

/* -------------------------------------------------------------------------------------------------
   "ON WHICH BOOKINGS" TICKS, on the activities screen
   Ticked means available, and everything starts ticked: she unticks the kinds of booking an activity
   does not belong to. Laid out as small pills so a row of five stays readable next to the activity
   it belongs to, on a screen that already has a column for direction.
   ------------------------------------------------------------------------------------------------- */
.ha-withset { display: flex; flex-wrap: wrap; gap: 4px 8px; }
.ha-withpill {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 0.8em; line-height: 1.3;
    padding: 3px 8px;
    border: 1px solid rgba(0, 0, 0, 0.18);
    border-radius: 20px;
    background: rgba(0, 0, 0, 0.03);
    cursor: pointer;
}
.ha-withpill input { margin: 0; }
/* A pill whose box is ticked reads as "on". :has() is supported everywhere this admin is used, and
   where it is not the checkbox itself still says which way it is set - so nothing depends on it. */
.ha-withpill:has(input:checked) { background: rgba(180, 40, 40, 0.10); border-color: var(--ha-accent-soft); }

/* "POSSIBLE WITH", folded away, because nearly every row is "Anyone"
   This column is about what can physically happen rather than about who she will see, so on 115 of
   117 rows it has nothing to say. Seven more pills on every row would drown the two that matter, so
   the summary carries the answer and the ticks open only when she wants them.
   A <details> needs no script: if one ever fails to load, the worst case is a list left open. */
.ha-genset > summary {
    cursor: pointer;
    font-size: 0.85em;
    line-height: 1.3;
    color: var(--ha-ink-muted);   /* 7.10:1 on --ha-card */
}
.ha-genset[open] > summary { margin-bottom: 6px; font-weight: 600; }
.ha-genset > summary::marker { color: var(--ha-accent-soft); }

/* -------------------------------------------------------------------------------------------------
   "WHAT I OFFER": the add row, the small buttons and the hidden list
   The Offer-it tick became a Remove button on 22/08/2026, so a removed row leaves the table and the
   list at the bottom is now the only way back. Each entry there carries its own button.
   ------------------------------------------------------------------------------------------------- */
.ha-btn-sm { padding: 4px 12px; font-size: .82em; }

.ha-addrow {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    margin: 10px 0 26px; padding-top: 10px;
    border-top: 1px dashed rgba(0, 0, 0, 0.16);
}

.ha-hiddenlist { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.ha-hiddenlist li {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 6px 0; border-bottom: 1px solid rgba(0, 0, 0, 0.10);
    margin: 0;
}
.ha-hiddenlist li:last-child { border-bottom: 0; }
.ha-hiddenlist form { margin: 0; }

/* ── The autosave tick on "What I offer" ────────────────────────────────────────────────────────
   Craig, 23/08/2026: the save button is gone from that screen and every box saves itself, so there
   has to be something on the page that says so. It sits beside the control she just touched.

   ⚠ Three states, not two. "Saved" is not the only outcome worth showing: a request the host
   refuses, or a session that has expired, has to look different from a success or she would go on
   working and lose the lot. Green for saved, red for not saved, and neither shows until the server
   has actually answered. */
.ha-saved {
    display: inline-block;
    min-width: 4.5em;
    margin-left: 0.5rem;
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.4;
    vertical-align: middle;
    white-space: nowrap;
}
.ha-saved-saving { color: var(--ha-muted, #8b8b8b); font-weight: 400; }
.ha-saved-ok     { color: #2e7d32; }
.ha-saved-bad    { color: #b42828; }

/* The line that replaces the button, so the screen still says how saving works. */
.ha-autosave-note { margin: 0 0 1rem; }

@media (prefers-reduced-motion: no-preference) {
    .ha-saved-ok { transition: opacity 0.3s ease; }
}

/* ── Sections on her page editor ────────────────────────────────────────────────────────────────
   Craig, 23/08/2026: she can reword any section title, hide a whole section, and add her own. The
   screen has to make a section LOOK like a section, or a title box reads as just another paragraph
   box with a short answer in it. */
.ha-section {
    margin: 1.75rem 0 0;
    padding: 0 0 0 0.9rem;
    border-left: 3px solid var(--ha-accent-soft, rgba(180, 40, 40, 0.35));
}
.ha-sectionhead {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.75rem;
}
.ha-sectionlabel {
    flex: 0 0 100%;
    margin: 0;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    opacity: 0.75;
}
.ha-sectiontitle {
    flex: 1 1 22rem;
    min-width: 0;
    font-size: 1.05rem;
    font-weight: 600;
}
/* The tag key: the value column is the half that matters, so let it breathe. */
.ha-table code { white-space: nowrap; }

@media (max-width: 575px) {
    .ha-section { padding-left: 0.6rem; }
    .ha-sectiontitle { flex-basis: 100%; }
}
