/* Oriental Spa — brand system, September 2026
 * GENERATED by build-brand-css.py. Do not hand-edit; edit the generator.
 * Load this LAST, after every other stylesheet.
 *
 * Inks (guidelines section 2). Hex comes from the guidelines table, never from a
 * CMYK conversion — naive conversion of C80 M20 Y100 K70 yields ~#0F3D00, not #004214.
 *   Dark Green #004214  primary — the mark, headings, secondary emphasis
 *   Green      #2D803B  accent, primary CTA — never the mark
 *   Grey       #939598  rules and dividers ONLY; 3.00:1 on white, fails body text
 *   Black      #231F20  renders #231F20, not #000000. That is correct.
 * Gold #C9A227 is a FOIL FINISH, not an ink, and has no token here — "Gold as
 * CMYK or hex is never permitted" (section 6).
 *
 * Type: Philosopher (SIL OFL). One display face and one body face; it does both.
 * Headings Bold, body Regular at 16px+ on screen, emphasis Italic.
 * Never fake-italicise or fake-bold — all four real files are self-hosted below.
 */

:root{
  --os-dark-green:#004214;
  --os-green:#2D803B;
  --os-grey:#939598;
  --os-black:#231F20;
  --os-paper:#FFFFFF;
  --os-font:'Philosopher',Georgia,serif;
}


/* --- Philosopher, self-hosted, true weights --- */
@font-face{font-family:'Philosopher';font-style:italic;font-weight:400;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Philosopher';font-style:italic;font-weight:400;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Philosopher';font-style:italic;font-weight:700;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Philosopher';font-style:italic;font-weight:700;font-display:swap;src:url('fonts/philosopher-700-italic-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:'Philosopher';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Philosopher';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Philosopher';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Philosopher';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/philosopher-700-normal-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;}

/* --- Retired faces, aliased across the full weight axis --- */
/* Philosopher */
@font-face{font-family:'Philosopher';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Philosopher';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Philosopher';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Philosopher';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Philosopher';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Philosopher';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Philosopher';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Philosopher';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}
/* Montserrat */
@font-face{font-family:'Montserrat';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Montserrat';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Montserrat';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Montserrat';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Montserrat';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Montserrat';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Montserrat';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Montserrat';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}
/* Noto Sans */
@font-face{font-family:'Noto Sans';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Noto Sans';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Noto Sans';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Noto Sans';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Noto Sans';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Noto Sans';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Noto Sans';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Noto Sans';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}
/* Playfair Display */
@font-face{font-family:'Playfair Display';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Playfair Display';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Playfair Display';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Playfair Display';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Playfair Display';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Playfair Display';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Playfair Display';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Playfair Display';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}
/* Lato */
@font-face{font-family:'Lato';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Lato';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Lato';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Lato';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Lato';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Lato';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Lato';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Lato';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}
/* Dancing Script */
@font-face{font-family:'Dancing Script';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Dancing Script';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Dancing Script';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Dancing Script';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Dancing Script';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Dancing Script';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Dancing Script';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Dancing Script';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}
/* Roboto */
@font-face{font-family:'Roboto';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Roboto';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Roboto';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Roboto';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Roboto';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Roboto';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Roboto';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Roboto';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}
/* Roboto Slab */
@font-face{font-family:'Roboto Slab';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Roboto Slab';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Roboto Slab';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Roboto Slab';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Roboto Slab';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Roboto Slab';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Roboto Slab';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Roboto Slab';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}
/* Vidaloka */
@font-face{font-family:'Vidaloka';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Vidaloka';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Vidaloka';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Vidaloka';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Vidaloka';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Vidaloka';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Vidaloka';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Vidaloka';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}
/* Poppins */
@font-face{font-family:'Poppins';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Poppins';font-style:normal;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-normal-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:'Poppins';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Poppins';font-style:italic;font-weight:100 500;font-display:swap;src:url('fonts/philosopher-400-italic-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:'Poppins';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Poppins';font-style:normal;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-normal-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:'Poppins';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-latin-ext.woff2') format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Poppins';font-style:italic;font-weight:600 900;font-display:swap;src:url('fonts/philosopher-700-italic-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;}

/* --- Anchored header -----------------------------------------------------
 * The nav stays reachable on a 10-screen home page. Astra's transparent header is
 * position:absolute over the hero, so it becomes `fixed` to keep the overlay while
 * anchoring; every other header is in flow and only needs `sticky`.
 */
.ast-primary-header-bar,.os-header{position:sticky;top:0;z-index:900;
  transition:background-color .2s ease,box-shadow .2s ease;}
body.ast-theme-transparent-header:not(.ast-header-break-point) .ast-primary-header-bar{
  position:fixed;top:0;left:0;right:0;width:100%;}
/* The hand-written header was sticky, i.e. IN FLOW, so on brochure and training
   it took 140px of document height BEFORE the banner -- and the banner then
   padded another 140 to clear a bar that was no longer over it. That double
   count is the grey strip between the bar and the photograph, and it pushed the
   title from y=221 (the spa menu) to y=336. Float it over the banner like
   Astra's and the two families measure the same.
   :has() scopes it to pages that actually have a banner; on a page without one
   a fixed bar would let the first paragraph slide under it. */
body:has(.os-banner) .os-header{
  position:fixed;top:0;left:0;right:0;width:100%;}

/* Past the hero the bar has body copy behind it, so it takes a solid ground. */
html.os-stuck .ast-primary-header-bar,
html.os-stuck .os-header{background:#FFFFFF!important;box-shadow:0 1px 0 #939598;}

@media (prefers-reduced-motion:reduce){
  .ast-primary-header-bar,.os-header{transition:none;}
}

/* --- Header lockup -------------------------------------------------------
 * Astra serves a TRANSPARENT header: at desktop widths it is position:absolute
 * over the hero, so the logo sits directly on a photograph. Dark Green on a
 * busy background is a section 6 "Never" — the reversed lockup is the supplied
 * answer, so both files ship and CSS picks one. No filter, no recolour: the
 * artwork is never altered, only selected.
 *
 * Astra's own JS puts .ast-header-break-point on <body> at mobile widths, where
 * the header becomes position:relative on white — so the dark lockup is correct
 * there, and on any page without .ast-theme-transparent-header.
 *
 * Size: 200px wide (160 mobile), against a 64px-wide minimum and 90 recommended
 * (section 4). Intrinsic width/height stay the artwork's own 672x120 and CSS sets
 * width with height:auto, so no squash can creep in.
 */
.custom-logo-link img,.custom-logo,.os-logo-rev{
  width:200px!important;max-width:200px!important;height:auto!important;display:block;}
.os-logo-rev{display:none!important;}

/* `os-overhero` is set by os-sticky-header.js only when a photograph or dark ground
   is MEASURED behind the bar. The body class was the old test and it lied: /services
   carries ast-theme-transparent-header but opens on white, so the reversed lockup
   rendered white-on-white and disappeared. Default is the dark lockup — the safe
   direction, since Dark Green on white is always legible. */
html.os-overhero:not(.ast-header-break-point) .custom-logo{display:none!important;}
html.os-overhero:not(.ast-header-break-point) .os-logo-rev{display:block!important;}

@media (max-width:480px){
  .custom-logo-link img,.custom-logo,.os-logo-rev{width:160px!important;max-width:160px!important;}
}

/* --- Header navigation ---------------------------------------------------
 * There was no colour rule for the nav links at all: they inherited Astra's white
 * from the transparent-header theme and KEPT it once the bar went solid white, so
 * the whole menu vanished on scroll and on any page that opens on white.
 *
 * Bound to the same measured `os-overhero` class as the lockup, so the type and the
 * mark can never disagree about what is behind them. Dark Green is the default —
 * legible on white, which is the state that cannot be got wrong.
 */
html body .main-header-menu .menu-link,
html body .main-header-menu > .menu-item > a{
  color:var(--os-dark-green)!important;transition:color .2s ease;}
html body .main-header-menu .menu-item:hover > .menu-link,
html body .main-header-menu .menu-item.current-menu-item > .menu-link{
  color:var(--os-green)!important;}

html.os-overhero body .main-header-menu .menu-link,
html.os-overhero body .main-header-menu > .menu-item > a{color:#FFFFFF!important;}
html.os-overhero body .main-header-menu .menu-item:hover > .menu-link,
html.os-overhero body .main-header-menu .menu-item.current-menu-item > .menu-link{
  color:#FFFFFF!important;text-decoration-color:#FFFFFF;}

/* The SECOND header. brochure, training, booking, gift-voucher and the voucher
 * pages use a hand-written .os-header instead of Astra's, and its own <style>
 * block hardcodes `.os-nav a{color:#fff}` with a text-shadow — white type for
 * ever, whatever is behind it. On those pages the bar sits on white and the menu
 * disappeared exactly like the Astra one did. !important because that rule lives
 * in a page <style> that loads after this file.
 *
 * The Book button keeps white type: it has its own green pill behind it.
 * The lockup needs no rule here — both variants are already in this markup, so
 * the os-overhero swap above covers it.
 */
/* $= not = : once a branch is active the Book Now href becomes
   /plattekloof/booking, and an exact match here would flip the button to dark
   green text on its own green pill. */
html body .os-header .os-nav a:not([href$="/booking"]){
  color:var(--os-dark-green)!important;text-shadow:none!important;}
html body .os-header .os-nav a:not([href$="/booking"]):hover{
  color:var(--os-green)!important;}
html.os-overhero body .os-header .os-nav a:not([href$="/booking"]){
  color:#FFFFFF!important;text-shadow:0 1px 3px rgba(0,0,0,.4)!important;}

@media (prefers-reduced-motion:reduce){
  html body .main-header-menu .menu-link{transition:none;}
}

/* Two headers re-set the wordmark in live type beside a lockup that already
 * carries it — section 6, "Never: re-set the wordmark in live type."
 *   .ast-site-title-wrap  Astra's header (index, about, contact, services, …)
 *   .os-logo-text         the custom header on booking, gift-voucher, brochure,
 *                         training, voucher-policy and voucher-payment
 * Hidden from sight, kept in the DOM: the Astra one carries schema.org
 * itemprop="name", and both give the link an accessible name. */
.ast-site-title-wrap,.os-logo-text{position:absolute!important;width:1px;height:1px;margin:-1px;
  padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;}

/* --- One button, eleven class names ------------------------------------
 * Measured before writing this: 11 distinct button classes across the site
 * (elementor-button, osl-btn, bf-btn, oblo-btn, osb-svcbtn, osb-btn, ocb-btn,
 * os-form-btn, os-cta-btn, opro-btn, os-card-btn) rendering at 8 different corner
 * radii — 30, 28, 24, 20, 8, 6, 5 and 3px. Eleven implementations of one control.
 *
 * They are not renamed or refactored: the markup is generated by Elementor and by
 * apiMenuPublish, so a rename would be undone on the next publish. Instead every
 * name resolves to the SAME two roles. Adding a twelfth class means adding it here,
 * not inventing a twelfth appearance.
 *
 *   primary  Green ground, white text  — 4.93:1, passes AA
 *   ghost    Dark Green outline on paper
 */
/* `html body` prefix is load-bearing. These pages carry inline <style> blocks
 * LATER in the document than this sheet, and a later !important at equal
 * specificity wins — 17 of 28 buttons were still ignoring the rule. Raising
 * specificity beats them without depending on load order, which a republish
 * from apiMenuPublish or Elementor could change again. */
html body .elementor-button,html body .osl-btn,html body .bf-btn,
html body .oblo-btn,html body .osb-svcbtn,html body .osb-btn,html body .ocb-btn,
html body .os-form-btn,html body .os-cta-btn,html body .opro-btn,
html body .os-card-btn,html body .osms-btn,
html body .btn,html body .btn-primary,html body .btn-secondary,
html body a.menu-link[href$="/booking"],html body .os-nav a[href$="/booking"]{
  border-radius:999px!important;
  font-family:var(--os-font)!important;
  font-weight:700!important;
  letter-spacing:.02em!important;
  line-height:1.2!important;
  border:1px solid rgba(255,255,255,.26)!important;
  /* The Book Now recipe, applied to every button: one soft drop shadow for lift
     and an inset top light for the edge. Measured off that button rather than
     invented, because it was the one that looked right. */
  box-shadow:0 8px 22px rgba(0,0,0,.20),inset 0 1px 0 rgba(255,255,255,.30)!important;
  transition:background-color .18s ease,color .18s ease,border-color .18s ease,
             transform .22s cubic-bezier(.34,1.4,.44,1),box-shadow .22s ease!important;
}
/* One press everywhere: the button dips and its shadow tightens, the same
   gesture the nav indicator makes when it fills with green. */
html body .elementor-button,html body .osl-btn,html body .bf-btn,
html body .oblo-btn,html body .osb-svcbtn,html body .osb-btn,html body .ocb-btn,
html body .os-form-btn,html body .os-cta-btn,html body .opro-btn,
html body .os-card-btn,html body .osms-btn,
html body .btn,html body .btn-primary,html body .btn-secondary{
  transform:translateZ(0);
}
html body .elementor-button:hover,html body .osl-btn:hover,html body .bf-btn:hover,
html body .oblo-btn:hover,html body .osb-svcbtn:hover,html body .osb-btn:hover,html body .ocb-btn:hover,
html body .os-form-btn:hover,html body .os-cta-btn:hover,html body .opro-btn:hover,
html body .os-card-btn:hover,html body .osms-btn:hover,
html body .btn:hover,html body .btn-primary:hover,html body .btn-secondary:hover{
  transform:translateY(-1px);
  box-shadow:0 12px 26px rgba(0,0,0,.24),inset 0 1px 0 rgba(255,255,255,.34)!important;
}
html body .elementor-button:active,html body .osl-btn:active,html body .bf-btn:active,
html body .oblo-btn:active,html body .osb-svcbtn:active,html body .osb-btn:active,html body .ocb-btn:active,
html body .os-form-btn:active,html body .os-cta-btn:active,html body .opro-btn:active,
html body .os-card-btn:active,html body .osms-btn:active,
html body .btn:active,html body .btn-primary:active,html body .btn-secondary:active{
  transform:translateY(1px) scale(.985);
  box-shadow:0 4px 12px rgba(0,0,0,.22),inset 0 1px 0 rgba(255,255,255,.22)!important;
}

/* Not every rounded thing is a call to action. A menu ROW and a calendar DAY are
   structure, and a 999px radius on either reads as a pill you are meant to press
   — a stadium calendar cell is just a circle. They were at 60px, 30px, 26px, 8px
   and 6px between them, which is the actual inconsistency; one secondary radius
   settles it without pretending they are buttons. */
html body .osms-row,html body .osb-day,html body .osb-slot,
html body .os-card,html body .osl-panel-card,html body .osms-card{
  border-radius:14px!important;}

/* Form fields are NOT buttons and must not become stadiums — a 999px radius on a
   text input reads as a pill you are meant to press. One modest radius instead. */
html body input[type="text"],html body input[type="email"],html body input[type="tel"],
html body input[type="number"],html body input[type="date"],html body select,
html body textarea{border-radius:12px!important;}
/* One focus treatment everywhere. Several of these had none at all, which is a
   keyboard-only user losing their place. */
html body .elementor-button:focus-visible,html body .osl-btn:focus-visible,
html body .bf-btn:focus-visible,html body .oblo-btn:focus-visible,
html body .osb-svcbtn:focus-visible,html body .osb-btn:focus-visible,
html body .ocb-btn:focus-visible,html body .os-form-btn:focus-visible,
html body .os-cta-btn:focus-visible,html body .opro-btn:focus-visible,
html body .os-card-btn:focus-visible,html body .osms-btn:focus-visible{
  outline:3px solid var(--os-dark-green)!important;
  outline-offset:2px!important;
}
@media (prefers-reduced-motion:reduce){
  html body .elementor-button,html body .osl-btn,html body .bf-btn,html body .oblo-btn,
  html body .osb-svcbtn,html body .osb-btn,html body .ocb-btn,html body .os-form-btn,
  html body .os-cta-btn,html body .opro-btn,html body .os-card-btn,
  html body .osms-btn{transition:none!important;}
}

/* --- Branch picker ---
 * The same idiom as the voucher card: white chips with green type that INVERT to
 * green-on-white when chosen, so the selected branch reads at a glance on paper
 * and on screen. Built on real radios so it is keyboard- and screen-reader-native;
 * the input is visually hidden, never display:none (that removes it from the tab
 * order and from the accessibility tree).
 */
html body .os-branch-pick{display:flex;flex-wrap:wrap;gap:8px;margin:4px 0 14px;}
html body .os-branch-pick > span{flex:1 1 150px;position:relative;display:block;}
html body .os-branch-pick input{position:absolute;width:1px;height:1px;opacity:0;
  margin:0;pointer-events:none;}
html body .os-branch-pick label{display:block;text-align:center;cursor:pointer;
  padding:11px 14px;border:1px solid var(--os-grey);border-radius:30px;
  background:#fff;color:var(--os-dark-green);font-family:var(--os-font);
  font-size:15px;line-height:1.2;transition:background .2s,color .2s,border-color .2s;}
html body .os-branch-pick input:checked + label{background:var(--os-green);
  border-color:var(--os-green);color:#fff;}
html body .os-branch-pick input:focus-visible + label{outline:3px solid var(--os-dark-green);
  outline-offset:2px;}
@media (prefers-reduced-motion:reduce){
  html body .os-branch-pick label{transition:none;}
}

/* A fixed header over a page that opens on copy rather than a hero overlaps the
   first thing on the page. os-sticky-header.js sets os-headroom (and the measured
   --os-header-h) only in that case, so pages that WANT the overlap keep it. */
html.os-headroom #content,html.os-headroom .site-content{
  padding-top:var(--os-header-h,88px);}
/* A branch fact that is a SENTENCE, not a banner. The tinted strip that used to
   carry these read as an ad; the facts themselves (training is Plattekloof only,
   a voucher is valid at both) are load-bearing and stay, in the page's own voice. */
html body .os-branchnote{max-width:900px;margin:0 auto 18px;padding:0 20px;
  font-size:15px;line-height:1.6;color:var(--os-black);}
html body .os-branchnote strong{color:var(--os-dark-green);}

/* --- The 922-1100px squeeze ---------------------------------------------
 * Astra swaps to its mobile header at 921px, so from 922px up the FULL desktop
 * nav has to fit — logo, seven links and the Book Now button. Between 922 and
 * about 1100 it does not: the button wraps to a second row and the bar goes from
 * 80px to 122px. Measured at 960px, and it is what a laptop at a half-screen
 * window actually gets. Because the title band's top padding is derived from the
 * header height, the band grew with it — one wrap, two visible faults.
 *
 * Tighten the row rather than add another breakpoint to Astra's: smaller type,
 * tighter gutters, smaller lockup — all still above the 64px lockup minimum.
 */
@media (min-width:922px) and (max-width:1100px){
  html body .ast-primary-header-bar .main-header-menu .menu-link{
    font-size:14px!important;padding-left:9px!important;padding-right:9px!important;}
  html body .ast-primary-header-bar .ast-custom-button,
  html body .ast-primary-header-bar .ast-builder-button-wrap .ast-custom-button,
  html body .ast-primary-header-bar .ast-header-button-1 .ast-custom-button{
    padding:9px 16px!important;font-size:14px!important;white-space:nowrap!important;}
  html body .custom-logo-link img,html body .custom-logo,html body .os-logo-rev{
    width:150px!important;max-width:150px!important;}
  html body .ast-primary-header-bar .site-header-primary-section-left,
  html body .ast-primary-header-bar .site-header-primary-section-right{
    flex-wrap:nowrap!important;}
}

/* --- Inner-page title band ----------------------------------------------
 * Every inner page opened with a 553px title band: 230px of top padding and
 * 190px of bottom around ~133px of words. Now that the photograph runs up under
 * the menu bar there is no white gap to fill, so the band only has to clear the
 * header and breathe a little. Top padding is the MEASURED header height plus
 * 40, so the title can never collide with the bar at any header size.
 * Saves ~240px on every one of these pages before a visitor scrolls.
 */
/* One band height on every inner page. It used to be padding plus whatever the
   title needed, so a two-line title made a taller band than a one-line one and
   the picture behind it changed size page to page. Fixed height, title centred in
   the space under the bar — home is the exception and keeps the full-page image.
   Scoped to BOTH headers, not just Astra's, or brochure and training would keep
   their own height. */
body:not(.home) #content .elementor-top-section:first-of-type{
  height:372px!important;min-height:372px!important;box-sizing:border-box!important;
  display:flex!important;align-items:center!important;justify-content:center!important;
  padding-top:calc(var(--os-header-h,80px) + var(--os-strip-h,64px))!important;
  padding-bottom:0!important;}
body:not(.home) #content .elementor-top-section:first-of-type > .elementor-container{
  width:100%;min-height:0!important;}
/* The banner's internals were inlined on each hand-written page, so a page that
   did not carry that <style> block could not use the band at all -- which is why
   promo had no band and its title sat at y=115, underneath a branch strip whose
   bottom edge is 140. Values are copied from those inline blocks, so the pages
   that already have them see no change. */
.os-banner{position:relative;overflow:hidden;}
/* The band pads 140px at the top to clear the bar, and the <img> is a flow
   child, so the photograph started BELOW that padding: 232px of picture with a
   140px strip of page ground above it, between the menu bar and the image. On
   the spa menu the same photograph is a background-image and fills the band, so
   only the hand-written pages showed the gap. Taken out of flow it fills the
   band and the padding goes on doing its job for the words. */
.os-banner > img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
.os-banner-overlay{position:absolute;inset:0;
  background:linear-gradient(to bottom,rgba(0,0,0,.55),rgba(0,0,0,.45));
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;}
.os-banner h1{color:#FFFFFF;text-transform:uppercase;letter-spacing:.05em;margin:0;}
/* The spa menu -- the reference -- renders its band divider at 0x0, i.e. the
   green rule under the title was already taken out there. This is the same
   ornament on the hand-written pages, so it goes the same way. */
/* A branch note INSIDE the band sits on the photograph, so it takes the band's
   white -- training had it at #231F20 with a Dark Green <strong>, both
   unreadable over the picture. The same note on a white page keeps its dark
   ink, which is why this is scoped to the band. */
.os-banner .os-branchnote,
.os-banner .os-branchnote strong,
.os-banner .os-branchnote a{color:#FFFFFF!important;}
.os-banner .os-branchnote a{text-decoration:underline!important;
  text-underline-offset:3px;text-decoration-color:rgba(255,255,255,.6)!important;}
body:not(.home) .os-banner-line{display:none!important;}
/* The overlay is absolutely positioned, so the band's own padding-top does not
   reach it and each page centred its title in a different place: 221 on the spa
   menu, 248 on brochure (a hardcoded 230px inline), 161 on promo. Give the
   overlay the same clearance the band uses and all four land together. */
body:not(.home) .os-banner-overlay{
  padding:calc(var(--os-header-h,80px) + var(--os-strip-h,64px)) 20px 0!important;}

/* The hand-written pages call their band .os-banner instead of an Elementor
   section — same band, same height, or brochure and training stay taller than
   the spa menu. */
body:not(.home) .os-banner{
  height:372px!important;min-height:372px!important;box-sizing:border-box!important;
  display:flex!important;align-items:center!important;justify-content:center!important;
  padding-top:calc(var(--os-header-h,80px) + var(--os-strip-h,64px))!important;
  padding-bottom:0!important;}
@media (max-width:921px){
  body:not(.home) #content .elementor-top-section:first-of-type,
  body:not(.home) .os-banner{height:268px!important;min-height:268px!important;}
}
/* The title itself was set for a 553px band. At this height it only has to name
   the page, so it comes down with the padding rather than filling the space.
   Applied to BOTH headers: brochure and training use the hand-written .os-header
   and were missed by a rule scoped to Astra's transparent-header class, which is
   why those two still had 90px titles. */
body:not(.home) #content .elementor-top-section:first-of-type h1,
body:not(.home) #content .elementor-top-section:first-of-type h2,
body:not(.home) .os-hero h1,body:not(.home) .os-hero h2,
body:not(.home) .os-banner h1,body:not(.home) .os-banner h2,
body:not(.home) .osl-hero h1{
  font-size:clamp(26px,3.4vw,44px)!important;line-height:1.12!important;margin:0!important;}

/* The home headline is the one place a big word is the point, but 90px was
   shouting — and it is the size that made the scroll-under collision obvious. */
body.home .elementor-top-section h1,
body.home .elementor-top-section .elementor-heading-title{
  font-size:clamp(32px,4.6vw,58px)!important;line-height:1.14!important;}
body.ast-theme-transparent-header:not(.home) #content .elementor-top-section:first-of-type
  .elementor-divider{padding-top:8px!important;padding-bottom:0!important;}
@media (max-width:921px){
  body.ast-theme-transparent-header:not(.home) #content .elementor-top-section:first-of-type{
    padding-top:calc(var(--os-header-h,80px) + 10px)!important;
    padding-bottom:18px!important;}
}

/* --- Home hero: two photographs, slow cross-fade, gentle Ken Burns ---------
 * Replaces a single 2019 WhatsApp photograph. Pure CSS: two layers on one 18s
 * cycle, the second offset by half, so one fades up as the other fades down and
 * each drifts from scale(1) to scale(1.09) while it is visible. The reset to
 * scale(1) happens at opacity 0, so it is never seen.
 *
 * The photographs are 2000px wide for a reason — the Ken Burns zoom crops into
 * them, so a source only as wide as the slot would go soft as it scaled.
 *
 * Decorative, so the markup is aria-hidden: the page's name is the h1, not the
 * picture behind it.
 */
/* background-COLOR, not just image:none. Two reasons, both found by testing.
   The header decides its colour by walking ancestors for the first background
   that paints; with the image removed and no colour, that walk ran past the
   section all the way to a white body and the nav turned green ON the
   photograph. It also stops a white flash before the first image decodes. */
/* The photograph is the page's ground, not a band at the top of it. Fixed to the
   viewport and behind everything, so it runs from under the menu bar all the way
   down to the footer and the white card floats on it rather than on a seam.
   Fixed also means it does not scroll, which suits a slow cross-fade — a drifting
   background that also slides is two motions fighting. */
body.home{background:#12290C;}
body.home .elementor-element-c50838b{background-image:none!important;
  background-color:transparent!important;}
body.home .os-hero-slides{position:fixed;inset:0;overflow:hidden;z-index:0;}
/* Everything between the bar and the footer has to be see-through or it paints
   over the picture. The footer keeps its own ground — it is where the image ends. */
body.home #content,body.home .site-content,body.home .ast-container,
body.home .entry-content,body.home .elementor,
body.home .elementor-element-c50838b > .elementor-container,
body.home #os-locations{background:transparent!important;}
body.home .site-footer,body.home footer{position:relative;z-index:2;}
/* THREE slides on a 24s cycle, so each holds for eight seconds. The delays are
   -8s and -16s: one third of the cycle apart, which is what keeps the fades
   evenly spaced. Change the count and all three numbers have to move together. */
body.home .os-hero-slide{position:absolute;inset:0;background-size:cover;
  background-position:center;opacity:0;will-change:opacity,transform;
  animation:osHeroFade 24s linear infinite,osHeroKen 24s ease-in-out infinite;}
body.home .os-hero-slide:nth-child(2){animation-delay:-8s;}
body.home .os-hero-slide:nth-child(3){animation-delay:-16s;}
/* The overlay travels with the picture, so the tint is even the whole way down
   instead of stopping where the hero section used to end. */
body.home .elementor-element-c50838b > .elementor-background-overlay{
  position:fixed!important;inset:0!important;z-index:1;}
body.home .elementor-element-c50838b > .elementor-container{position:relative;z-index:2;}

/* A third of 24s is 33.3%. Up by 4%, hold to 29%, down by 37% — so each slide is
   fully visible while the next is still invisible, and only one pair ever
   cross-fades at a time. */
@keyframes osHeroFade{
  0%{opacity:0;} 4%{opacity:1;} 29%{opacity:1;} 37%{opacity:0;} 100%{opacity:0;}
}
/* The drift finishes before the slide fades out, so the snap back to scale(1)
   always happens at opacity 0. 1.06 rather than 1.09: two of these frames come
   from the spa menu at 1600 and 1536 wide, and a bigger zoom would magnify the
   softness it is meant to disguise. */
@keyframes osHeroKen{
  0%{transform:scale(1);} 37%{transform:scale(1.06);} 100%{transform:scale(1.06);}
}

/* A drifting, cross-fading photograph is exactly the motion 2.3.3 asks you to
   drop. One still frame, no animation. */
@media (prefers-reduced-motion:reduce){
  body.home .os-hero-slide{animation:none!important;opacity:0;}
  body.home .os-hero-slide:first-child{opacity:1;}
}

/* --- Home: choose a branch, above the fold -------------------------------
 * The home page was 8.5 screens and buried the branch choice at y=866 — below
 * the fold on a 900px viewport, so the first decision a visitor has to make was
 * the one thing they could not see. Everything below the chooser duplicated a
 * page already in the nav, so it is gone; what remains is the hero and the
 * chooser, and the hero is capped so both fit one screen.
 *
 * Measured at 1440x900: chooser 492px + footer 121px, so a hero of 42vh (378px)
 * puts the branch tabs AND both call-to-action buttons inside the first screen.
 * Scoped to body.home so no other page's hero changes.
 */
/* The 866px hero was 510px of PADDING — 270 top, 240 bottom — around 356px of
   content. Capping min-height did nothing because the height never came from
   min-height. Budget at 1440x900: hero 56+288+40 = 384, chooser ~480, total 864,
   inside the 900 fold with both buttons visible. */
/* The hero now sits UNDER a fixed header and a fixed branch strip, so its top
   padding has to clear both — measured, not guessed, because the centred header
   is 161px at desktop and the strip about 67, and both move with width. */
/* Home is one full-screen photograph with a white card floating on it, rather
   than a band of image above a white page. The card is the branch panel — it was
   already a rounded white box, it just used to sit below the picture. */
body.home .elementor-top-section{min-height:64svh!important;
  display:flex!important;align-items:flex-start!important;
  padding-top:calc(var(--os-header-h,80px) + var(--os-strip-h,0px) + 40px)!important;
  padding-bottom:16px!important;}
body.home .elementor-top-section > .elementor-container{
  min-height:0!important;max-height:none!important;width:100%;}

/* The panel overlaps the hero instead of following it. Negative margin pulls it
   up over the photograph; the section below keeps room so nothing collides. */
body.home #os-locations{margin-top:-210px!important;position:relative;z-index:5;
  background:transparent!important;padding-top:0!important;}
body.home #os-locations .osl-inner{max-width:980px;}
body.home #os-locations .osl-panel{
  background:#FFFFFF!important;border-radius:22px!important;
  box-shadow:0 26px 60px rgba(0,0,0,.28),0 6px 16px rgba(0,0,0,.12)!important;
  border:1px solid rgba(255,255,255,.7)!important;padding:30px 34px!important;}
@media (max-width:921px){
  body.home .elementor-top-section{min-height:64svh!important;}
  body.home #os-locations{margin-top:-120px!important;}
  body.home #os-locations .osl-panel{padding:22px 20px!important;border-radius:18px!important;}
}
/* The card was landing 18px off the footer, which read as the page running out
   rather than ending. 64px below, top unchanged because the card is pulled up
   over the photograph and its top spacing comes from that negative margin. */
body.home #os-locations{padding-top:18px!important;padding-bottom:64px!important;}
@media (max-width:921px){
  /* On a phone the fold is a different promise — let the hero breathe and let
     the chooser follow it rather than squeezing both into a short viewport. */
  body.home .elementor-top-section{
    padding-top:calc(var(--os-header-h,80px) + var(--os-strip-h,0px) + 24px)!important;
    padding-bottom:32px!important;}
  body.home .elementor-top-section > .elementor-container{
    min-height:34vh!important;max-height:none!important;}
}

/* --- The bar as a floating pill ------------------------------------------
 * The header stops being a full-width band and becomes one long pill floating
 * over the page, in the same family as the branch switch and the Book Now
 * button: same radius, same rim, same shadow grammar.
 *
 * The pill is the INNER container, not the bar itself — the bar stays full width
 * and keeps doing the sticky/fixed work, so none of the header logic changes.
 *
 * "Notches": each nav item is a segment inside the pill rather than loose text,
 * so the current page and whatever is hovered read as seated in a slot. Only
 * those two are filled — filling all seven would make a toolbar.
 */
@media (min-width:922px){
  html body .ast-primary-header-bar > .site-primary-header-wrap,
  html body .os-header > .os-header-inner{
    max-width:1200px;margin:0 auto!important;padding:6px 20px!important;}

  /* The notches. */
  html body .main-header-menu .menu-link{border-radius:999px;
    padding:8px 15px!important;transition:background-color .22s ease,color .22s ease;}
  /* Snug, not stretched: min-width was padding short words out to 74px, which is
     what made the indicator square enough to look like a circle. */
  html body .main-header-menu .menu-link{text-align:center;padding:9px 14px!important;}

  /* The travelling notch. One pill, positioned by os-sticky-header.js, that
     glides to whatever is hovered and returns to the current page — the same
     gesture as the branch switch's thumb. It sits BEHIND the labels, so the
     text colour never changes as it passes.
     Book Now is excluded in the script: it is already a filled button. */
  /* 999px, matching Book Now -- Laurent's call, 17 Sep. Everything else in the
     bar is already a stadium (Book Now, the branch pill, its two segments and
     the sliding thumb); the indicator was the only 12px box in the row.
     This reverses the earlier 12px, which was a reaction to it reading as a
     circle. It no longer can: the indicator is clamped to 38px tall and the
     narrowest label, "Home", is 68px wide, so the roundest it ever gets is a
     1.8:1 stadium against Book Now's 2.75:1. A circle needs width == height. */
  html body .os-nav-ind{position:absolute;left:0;top:0;z-index:0;pointer-events:none;
    border-radius:999px;opacity:0;
    background:rgba(0,66,20,.07);border:1px solid rgba(0,66,20,.14);
    box-shadow:0 2px 8px rgba(0,66,20,.08);}
  html.os-overhero body .os-nav-ind{
    background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.26);
    box-shadow:0 8px 22px rgba(0,0,0,.22),inset 0 1px 0 rgba(255,255,255,.30);}

  /* Pressed: the indicator fills with Green and the label goes white under it.
     Same colour the branch thumb and Book Now use, so a press anywhere in the
     header resolves to the same green. */
  html body .os-nav-ind{transition:background-color .18s ease,border-color .18s ease;}
  html body .os-nav-ind.is-press{
    background:var(--os-green)!important;border-color:var(--os-green)!important;
    box-shadow:0 8px 22px rgba(0,0,0,.30),inset 0 1px 0 rgba(255,255,255,.30)!important;}
  html body .main-header-menu .menu-link:active,
  html body .os-header .os-nav a:active{color:#FFFFFF!important;}
  @media (prefers-reduced-motion:reduce){
    html body .os-nav-ind{transition:none;}
  }
  html body .main-header-menu > .menu-item,
  html body .os-header .os-nav > a{position:relative;z-index:1;}
}

/* --- Header: glass over a hero -------------------------------------------
 * The bar keeps its WIDE stance — lockup left, nav right. Centring it was tried
 * and stacked into two rows, which cost 80px of height and read worse.
 *
 * Over a hero the bar is transparent and fixed, so anything scrolling underneath
 * used to pass straight through it — the 90px hero title sliding up through the
 * menu was the collision. It now tints and blurs what goes behind it, the same
 * treatment as the branch pill, which is both the fix and the consistency: one
 * glass language for the two things floating over the photograph.
 */
/* The bar is ALWAYS dark glass, on every page and at every scroll position.
 *
 * It used to switch to solid white once you scrolled past the hero, which meant
 * the header looked like two different components depending on where you were —
 * and the page you were reading changed its own chrome under you. Holding one
 * treatment is what makes home, contact and booking feel like one site.
 *
 * It keeps a blurred crop of whatever is behind it, so over a hero it is filled
 * with the photograph and over copy it is a dark frosted bar.
 */
/* The two headers measured 111px (Astra) and 53px (the hand-written one), which
   is why the distance from the bar to the content differed between home and, say,
   brochure. One height for both, set on the inner wrapper so the bar itself still
   measures whatever the browser makes it and --os-header-h stays honest. */
html body .ast-primary-header-bar > .site-primary-header-wrap,
html body .os-header > .os-header-inner{
  height:88px!important;min-height:88px!important;
  display:flex!important;align-items:center!important;
  padding-top:0!important;padding-bottom:0!important;}
html body .ast-primary-header-bar .ast-builder-grid-row,
html body .ast-primary-header-bar .ast-builder-layout-element,
html body .ast-primary-header-bar .site-header-section{
  min-height:0!important;height:auto!important;padding-top:0!important;padding-bottom:0!important;}
@media (max-width:921px){
  html body .ast-primary-header-bar > .site-primary-header-wrap,
  html body .os-header > .os-header-inner{min-height:66px!important;}
}

/* Centring the nav in the space BESIDE the lockup is not the same as centring it
   on the page: a 200px lockup pushed it 13px off. Above 1200px it is centred on
   the VIEWPORT and the lockup sits beside it; below that the two would collide,
   and the 922-1100 range is already tight. */
/* The nav is centred on the VIEWPORT while its wrapper is capped at 1200px, so
   the lockup sits 200px inside the page edge and the nav's left edge does not
   move with the viewport relative to it: at 1600 the Astra nav overlapped the
   lockup by 9px and the hand-written one by 45px, and the arithmetic says that
   gap is constant above the cap rather than opening up on a wider screen. So
   the cap is the problem. Above the breakpoint the bar runs the full width with
   a 32px gutter, which puts the lockup at the page edge and leaves the middle
   free. */
@media (min-width:1280px){
  html body .ast-primary-header-bar > .site-primary-header-wrap,
  html body .os-header > .os-header-inner{
    max-width:none!important;width:100%!important;
    padding-left:32px!important;padding-right:32px!important;}
}
/* 1280, not 1200: with a 778px nav, a 200px lockup and a 32px gutter the row
   needs 778 + 2x248 = 1274px before the two can clear each other. Between 1200
   and 1274 they cannot, so the nav stays in normal flow there, as it does below
   1200. */
@media (min-width:1280px){
  html body .ast-primary-header-bar > .site-primary-header-wrap,
  html body .os-header > .os-header-inner{position:relative;}
  html body .ast-primary-header-bar .site-header-primary-section-right,
  html body .os-header .os-nav{
    position:absolute!important;left:50%!important;transform:translateX(-50%)!important;
    flex:0 0 auto!important;justify-content:center!important;
    /* max-content, NOT auto. An absolutely positioned box shrink-to-fits against
       the space LEFT of its containing block's right edge, and left:50% leaves
       exactly half -- 600px of a 1200px wrap. The seven items measure 695px, so
       the list wrapped and Book Now dropped onto a second row, on top of the
       branch pill. width:auto reproduces that; max-content does not. */
    width:max-content!important;max-width:none!important;}
}
/* Belt and braces: nothing in this bar may wrap, at any width. A menu that
   wraps inside an 88px bar does not grow the bar -- it overflows it upwards,
   which is why the first row measured at y=-27. */
html body .main-header-menu,html body .main-navigation,
html body .os-header .os-nav{flex-wrap:nowrap!important;}
html body .main-header-menu > .menu-item > a,
html body .os-header .os-nav > a{white-space:nowrap!important;}

html body .ast-primary-header-bar,
html body .os-header,
html.os-stuck body .ast-primary-header-bar,
html.os-stuck body .os-header{
  background:linear-gradient(180deg,rgba(35,31,32,.72),rgba(35,31,32,.56))!important;
  -webkit-backdrop-filter:blur(18px) saturate(140%);
  backdrop-filter:blur(18px) saturate(140%);
  border-bottom:1px solid rgba(255,255,255,.14)!important;
  box-shadow:0 6px 20px rgba(0,0,0,.18)!important;}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  html body .ast-primary-header-bar,html body .os-header{
    background:linear-gradient(180deg,rgba(35,31,32,.88),rgba(35,31,32,.78))!important;}
}

/* One treatment means one set of colours: white type and the reversed lockup
   everywhere, rather than a probe deciding per page. */
html body .main-header-menu .menu-link,
html body .main-header-menu > .menu-item > a,
html body .os-header .os-nav a:not([href$="/booking"]){color:#FFFFFF!important;}
html body .main-header-menu .menu-item:hover > .menu-link,
html body .main-header-menu .menu-item.current-menu-item > .menu-link{color:#FFFFFF!important;}
html body .custom-logo{display:none!important;}
html body .os-logo-rev{display:block!important;}
html body .os-nav-ind{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.26);
  box-shadow:0 8px 22px rgba(0,0,0,.22),inset 0 1px 0 rgba(255,255,255,.30);}

/* --- One glass language across the bar ------------------------------------
 * The pill and the menu bar are the two things floating on the photograph, so
 * they share a vocabulary rather than each having their own: the same rim light,
 * the same blur, the same pill geometry.
 *
 * Restrained on purpose. The nav gets a pill only on hover and for the current
 * page — pills on all seven links would read as a toolbar, not a menu.
 */
html.os-overhero body .ast-primary-header-bar{
  border-bottom:1px solid rgba(255,255,255,.16);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.18);}

/* No per-item fills: the travelling indicator is the only highlight, or the two
   fight and the current page lights up twice. Same reason the underline goes —
   the indicator already says which page you are on, and an underline under a
   highlighted item is the third way of saying it. */
html.os-overhero body .main-header-menu .menu-link,
html.os-overhero body .os-header .os-nav a:not([href$="/booking"]){
  transition:color .25s ease;}
html body .main-header-menu .menu-link,
html body .main-header-menu .menu-item.current-menu-item > .menu-link,
/* The current page's link still carried a green text-decoration underline from
   the theme -- state-dependent, so it only showed on whichever item was flagged
   current and slipped past the earlier sweep. No nav link is underlined in any
   state; the travelling indicator is what says where you are. */
html body .main-header-menu a,html body .main-header-menu .menu-link,
html body .main-header-menu .current-menu-item > a,
html body .main-header-menu .current_page_item > a,
html body .os-header .os-nav a,html body .os-mobile-menu a{
  text-decoration:none!important;text-decoration-line:none!important;}
/* Astra's items carry 14px of side padding and the hand-written ones carried
   20px, which is the whole 72px difference between a 778px nav and an 850px
   one -- and 850 is what made the lockup collision twice as bad on brochure. */
html body .os-header .os-nav > a:not([href$="/booking"]){
  padding-left:14px!important;padding-right:14px!important;}
html body .os-header .os-nav a{text-decoration:none!important;}
html body .main-header-menu .menu-link:hover,
html body .os-header .os-nav a:hover{text-decoration:none!important;}

/* Book Now is already a pill; over the photograph it picks up the same rim and
   depth as the branch thumb so the two read as one family. */
html.os-overhero body .main-header-menu a.menu-link[href$="/booking"],
html.os-overhero body .os-header .os-nav a[href$="/booking"]{
  box-shadow:0 8px 22px rgba(0,0,0,.30),inset 0 1px 0 rgba(255,255,255,.30);
  border:1px solid rgba(255,255,255,.26);}

/* --- Branch strip ---------------------------------------------------------
 * Sits directly under the menu bar on every page except home, and answers one
 * question at a glance: which spa am I reading about. Sticky under the header
 * so the answer is still there after scrolling.
 *
 * Kept to ~38px because the whole point of the last pass was buying vertical
 * space back; a second full bar would spend it again. Solid ground rather than
 * transparent, so it is legible over a photograph as well as over copy.
 */
/* 930, above the header's 900. The pill is meant to straddle the bar, and at
   880 the hand-written header painted its dark glass over the pill's top half
   -- elementsFromPoint at the pill's centre returned .os-header first. Astra's
   bar sits inside a wrapper that makes its own stacking context, so there the
   pill was already on top and only brochure and its family looked sunken. The
   strip is pointer-events:none apart from the pill itself, so raising it cannot
   swallow a nav click. */
/* top MUST match the -22px margin below, or sticky fights its own flow
   position: the margin puts the strip's natural top at header-22, while
   top:header pins it 22px LOWER as soon as it sticks -- which is immediately,
   at scrollTop 0. On a page with no banner that pushed the pill down over the
   first line of copy (gift-voucher: pill 89-140, the branch note at 118). Same
   number in both places, so pinned and unpinned are the same place. */
html body .os-branch-strip{position:sticky;top:calc(var(--os-header-h,80px) - 22px);z-index:930;
  display:flex;align-items:center;justify-content:center;
  margin-top:-22px;padding:0 16px 12px;background:transparent;border-bottom:0;
  pointer-events:none;
  font-family:var(--os-font);font-size:14px;line-height:1.2;}
html body .os-branch-pill{pointer-events:auto;}

/* Over a hero the strip has no ground of its own AND takes no space: the header
   is fixed over the photograph there, so a strip in normal flow would push the
   hero down and the image would stop running under the menu bar. Fixed under the
   header instead, so the slideshow passes beneath both and only the glass pill
   is opaque. */
/* Tucked up close under the bar rather than floating in the middle of the gap —
   it reads as part of the header furniture instead of a separate row. */
/* Keyed to os-strip-float, which is decided once at load, NOT to os-overhero,
   which flips as you scroll. Keying it to the scroll state moved the strip
   between out-of-flow and in-flow mid-scroll and shifted the whole page 118px.
   See the note in os-sticky-header.js. */
html.os-strip-float body .os-branch-strip{position:fixed;left:0;right:0;
  top:calc(var(--os-header-h,80px) - 20px);margin-top:0!important;
  background:transparent;border-bottom:0;
  padding-top:0;padding-bottom:14px;pointer-events:none;}
html.os-strip-float body .os-branch-pill{pointer-events:auto;}

/* The segmented pill. One dark track, two equal halves, a green thumb that slides.
 *
 * Colours are brand inks, not invented UI greys: the track is Black #231F20 and
 * the resting label is Grey #939598, which measures 5.2:1 on it — Grey fails as
 * text on WHITE, which is why the guidelines restrict it, but on near-black it is
 * comfortably above the 4.5:1 body minimum. The chosen half is Green with white.
 *
 * Equal halves are what makes the slide possible: a thumb cannot line up with
 * segments sized to their own words. The thumb is positioned from data-active, an
 * INDEX, so a third branch needs no new rule — only the width fraction changes.
 */
/* Glass, in the Apple sense: the pill does not carry its own colour so much as
   tint and blur whatever is behind it. Over the hero that is the photograph, and
   the slideshow keeps moving underneath it.
   backdrop-filter is unevenly supported, so the rgba fallback is dark enough to
   hold white and grey text on its own — @supports only DEEPENS the effect. */
html body .os-branch-pill{position:relative;display:inline-flex;align-items:stretch;
  background:rgba(35,31,32,.78);border:1px solid rgba(255,255,255,.26);
  border-radius:999px;padding:3px;isolation:isolate;
  box-shadow:0 8px 22px rgba(0,0,0,.30),inset 0 1px 0 rgba(255,255,255,.30);}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  /* More blur, more saturation, and a brighter rim: the glass was reading as a
     flat dark pill because the edge had nothing to catch. The top highlight and
     the bottom shade are what make it look like a lens rather than a panel. */
  /* Blur stays, but light — it is what separates the track from a flat bar. The
     tint is high enough that the look survives without it. */
  html body .os-branch-pill{background:rgba(35,31,32,.58);
    -webkit-backdrop-filter:blur(14px) saturate(140%);
    backdrop-filter:blur(14px) saturate(140%);}
}
/* On a SOLID track the resting label is brand Grey at a measured 5.43:1. Over
   glass the ground is a photograph and that number stops being a promise — a
   pale frame would leave grey-on-pale. White at 78% keeps the resting/chosen
   distinction without betting legibility on what the slideshow is showing. */
html.os-overhero body .os-branch-seg{color:rgba(255,255,255,.88);
  text-shadow:0 1px 3px rgba(0,0,0,.45);}
html.os-overhero body .os-branch-seg.is-on,
html.os-overhero body .os-branch-seg:hover{color:#FFFFFF;}
html body .os-branch-thumb{position:absolute;z-index:0;top:3px;bottom:3px;left:3px;
  width:calc((100% - 6px) / 2);border-radius:999px;background:var(--os-green);
  border:1px solid rgba(255,255,255,.26);
  box-shadow:0 8px 22px rgba(0,0,0,.30),inset 0 1px 0 rgba(255,255,255,.30);
  transition:transform .46s cubic-bezier(.34,1.56,.42,1);}
@media (prefers-reduced-motion:no-preference){
  html body .os-branch-pill[data-active] .os-branch-thumb{animation:osThumbSquish .46s ease;}
}
/* The thumb stretches slightly along the direction of travel and settles — the
   "liquid" part of a liquid tab bar. Purely decorative, so it is the first thing
   prefers-reduced-motion drops. */
@keyframes osThumbSquish{
  0%{transform-origin:center;}
  40%{scale:1.06 .94;}
  100%{scale:1 1;}
}
html body .os-branch-pill[data-count="3"] .os-branch-thumb{width:calc((100% - 6px) / 3);}
html body .os-branch-pill[data-active="1"] .os-branch-thumb{transform:translateX(100%);}
html body .os-branch-pill[data-active="2"] .os-branch-thumb{transform:translateX(200%);}

/* Brand Grey measured 5.43:1 on the solid track — legal, but it read as disabled
   rather than as the other choice. White at 78% is brighter, still clearly the
   resting state next to a solid white label, and it is a tint of an ink rather
   than a fifth grey invented for one control. */
html body .os-branch-seg{position:relative;z-index:1;flex:1 1 0;min-width:112px;
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  padding:7px 16px;border-radius:999px;text-decoration:none;white-space:nowrap;
  color:rgba(255,255,255,.78);
  transition:color .25s ease,transform .3s cubic-bezier(.4,1.3,.5,1);}
html body .os-branch-seg.is-on{transform:scale(1.03);}
html body .os-branch-seg:active{transform:scale(.97);}
html body .os-branch-seg:hover{color:#FFFFFF;}
html body .os-branch-seg.is-on{color:#FFFFFF;}
html body .os-branch-seg:focus-visible{outline:2px solid #FFFFFF;outline-offset:-3px;}
html body .os-branch-soon{font-size:10px;letter-spacing:.08em;text-transform:uppercase;
  padding:2px 7px;border-radius:20px;background:rgba(255,255,255,.14);color:inherit;}
html body .os-branch-seg.is-on .os-branch-soon{background:rgba(0,0,0,.22);color:#FFFFFF;}

@media (prefers-reduced-motion:reduce){
  html body .os-branch-thumb,html body .os-branch-seg{transition:none;}
}
@media (max-width:480px){
  html body .os-branch-strip{gap:8px;padding:6px 10px;}
  html body .os-branch-strip-label{display:none;}
  html body .os-branch-seg{min-width:0;padding:7px 12px;font-size:13px;}
  html body .os-branch-soon{display:none;}
}

/* The green rule under each page title is the same green line, just decorative
   rather than a link underline. It went from under "Welcome to" on home; it goes
   from under every other page title for the same reason. */
/* Scoped to the title band, this left the same ornament under every SECTION
   heading further down -- "Spa Brochures", "Book an Appointment", "Oriental Spa
   Training Centre" and 24 .os-gold-line rules besides. They are all the same
   decoration, so they all go. (.os-gold-line is doubly wrong: gold is a foil
   finish in this brand and is never a screen fill.) */
html body .elementor-widget-divider,
html body .elementor-divider,
html body .os-gold-line,html body .os-gold-line-w,
html body .os-banner-line,
html body .os-section-line,html body .os-divider{display:none!important;}

/* [hidden] loses to ANY explicit display, and the UA sheet's [hidden]{display:none}
 * is only a normal declaration -- so `.ocb-status{display:inline-block}` beat it
 * and the Brackenfell "Opening Sat 26 Sep" badge rendered beside "Open now" on a
 * page showing Plattekloof. data-os-only sets el.hidden, so EVERY branch-gated
 * block on the site is one display declaration away from the same bug. Settle it
 * once, for all of them.
 */
html body [hidden]{display:none!important;}

/* An injected <section> inside an Elementor widget-wrap is a flex item with no
 * width of its own, so it shrank to its content -- 405px of a 1140px column,
 * with the column's green ground showing beside it. Elementor's own widgets are
 * width:100%; ours has to say so too.
 */
html body .elementor-widget-wrap > section[id^="os-"]{flex:0 0 100%!important;width:100%!important;}

/* The "photograph to follow" line under a page title. HTML rather than part of
 * the placeholder image, because background-size:cover crops that image to a
 * different slice at every viewport and anything positioned inside it can
 * vanish. White, because it sits on the band with the title.
 */
html body .os-banner-note{margin:10px 0 0;text-align:center;color:#FFFFFF;
  font-family:var(--os-font);font-size:17px;letter-spacing:.04em;opacity:.92;}
html body .os-banner-note .os-banner-note-sub{display:block;margin-top:3px;
  font-size:14px;letter-spacing:.02em;opacity:.78;}

/* The branch's own line on the home card, where the duplicate Book button was.
 * Measure capped so it stays readable next to the fact columns above it.
 */
html body .osl-about{margin:2px 0 16px;max-width:62ch;
  font-family:var(--os-font);font-size:16px;line-height:1.6;color:#231F20;}
/* Reads as part of the sentence rather than as a third button -- the card
   already carries one, and the whole point of this pass was fewer of them. */
html body .osl-more{margin-left:.35em;color:#2D803B;font-weight:700;
  text-decoration:none;white-space:nowrap;}
/* A literal arrow character, deliberately, rather than a CSS unicode escape.
   The escape form was written into this file from another Python string, and
   because this file's CSS also lives inside a Python string it was parsed a
   second time: the backslash-two-one prefix is a valid OCTAL escape, so it
   became one control character followed by the leftover digits, and the link
   rendered as "Read more 92". Writing the character itself cannot be
   re-interpreted by anything. */
html body .osl-more::after{content:" →";}
html body .osl-more:hover,html body .osl-more:focus-visible{
  text-decoration:underline;text-underline-offset:3px;}

/* --- Touch targets on a phone ---------------------------------------------
 * Audited at 375x812 across nine pages. No page scrolls sideways, but every one
 * had between 9 and 18 controls under 40px: the footer policy links at 22, the
 * branch pill's two segments at 30, the spa-menu filter chips at 35, and the
 * social icons at 36. A finger is about 44px.
 *
 * Scoped to the phone breakpoint on purpose. On a mouse these sizes are fine and
 * growing them would loosen a layout that is currently tight and deliberate.
 * Links inside running prose are left alone -- padding those out would break the
 * line box they sit in, and they are read, not aimed at.
 */
@media (max-width:768px){
  html body .os-footer-links{display:flex;flex-wrap:wrap;justify-content:center;
    gap:2px 4px;}
  html body .os-footer-links a{min-height:44px;display:inline-flex;align-items:center;
    padding:0 12px;}

  html body .os-branch-seg{min-height:40px!important;padding-top:0!important;
    padding-bottom:0!important;display:inline-flex!important;align-items:center!important;}
  html body .os-branch-pill{padding:4px!important;}
  html body .os-branch-thumb{min-height:40px!important;}

  html body .osms-pill{min-height:42px!important;padding-top:0!important;
    padding-bottom:0!important;display:inline-flex!important;align-items:center!important;}

  html body .ocb-social a,html body .os-social a{width:44px!important;height:44px!important;}

  /* The booking calendar's own controls: the month and year selects were 28px
     and the prev/next arrows 36px, on the page where a mis-tap costs a booking. */
  html body .osb-calnav{min-width:44px!important;min-height:44px!important;
    display:inline-flex!important;align-items:center!important;justify-content:center!important;}
  html body .osb-headsel{min-height:44px!important;}

  /* The lockup is a link too, and at 29px it was the smallest thing in the bar. */
  html body .custom-logo-link,html body .os-logo{min-height:44px;display:inline-flex;
    align-items:center;}
}

/* --- ONE button, everywhere -----------------------------------------------
 * Audited across the site before writing this: heights ran 35 to 52, fonts 13
 * to 16px, some uppercase and some not, one button in Noto Sans, one on Black
 * instead of Green, one at a 30px radius, and sixteen classless anchors with
 * the whole look in an inline style -- unreachable by any stylesheet, which is
 * exactly the failure this codebase has hit before. Those sixteen now carry
 * .os-btn and keep only their layout declarations.
 *
 * The recipe is Book Now's, measured rather than guessed: Green ground, a
 * light edge, a soft drop and an inset top highlight. That pair of shadows is
 * what reads as glass -- the inset line catches the top edge, the drop lifts it
 * off the page.
 */
:root{
  --os-btn-h:46px;
  --os-btn-shadow:0 8px 22px rgba(0,0,0,.20),inset 0 1px 0 rgba(255,255,255,.30);
  --os-btn-shadow-hover:0 12px 28px rgba(0,0,0,.26),inset 0 1px 0 rgba(255,255,255,.40);
}
html body .os-btn,
html body .osms-btn,
html body .os-card-btn,
html body .os-cta-btn,
html body .oblo-btn,
html body .ocb-btn,
html body .ovou-btn,
html body .bf-btn,
html body .opro-btn,
html body .os-form-btn,
html body .osl-btn,
html body .osb-btn,
html body .osb-svcbtn,
html body .bk-btn,
html body .cv-btn,
html body .lp-btn,
html body .rr-btn,
html body .sr-btn,
html body .os-login-btn,
html body .os-install-btn,
html body .os-contact-grid .occ-form-actions .ocb-btn{
  display:inline-flex!important;align-items:center!important;justify-content:center!important;
  min-height:var(--os-btn-h)!important;padding:0 22px!important;
  border-radius:999px!important;
  background:#2D803B!important;color:#FFFFFF!important;
  border:1px solid rgba(255,255,255,.26)!important;
  box-shadow:var(--os-btn-shadow)!important;
  font-family:var(--os-font)!important;font-size:16px!important;font-weight:700!important;
  letter-spacing:.02em!important;text-transform:none!important;text-decoration:none!important;
  cursor:pointer;
  transition:background-color .18s ease,border-color .18s ease,
             transform .22s cubic-bezier(.34,1.4,.44,1),box-shadow .22s ease!important;}

html body .osl-btn:hover,html body .osb-btn:hover,html body .osb-svcbtn:hover,
html body .bk-btn:hover,html body .cv-btn:hover,html body .lp-btn:hover,
html body .rr-btn:hover,html body .sr-btn:hover,html body .os-login-btn:hover,
html body .os-btn:hover,html body .osms-btn:hover,html body .os-card-btn:hover,
html body .os-cta-btn:hover,html body .oblo-btn:hover,html body .ocb-btn:hover,
html body .ovou-btn:hover,html body .bf-btn:hover,html body .opro-btn:hover,
html body .os-form-btn:hover{
  background:#246A31!important;transform:translateY(-1px);
  box-shadow:var(--os-btn-shadow-hover)!important;}
/* Press returns it to the page -- the same gesture the nav indicator makes. */
html body .osl-btn:active,html body .osb-btn:active,html body .bk-btn:active,
html body .cv-btn:active,html body .sr-btn:active,
html body .os-btn:active,html body .osms-btn:active,html body .os-card-btn:active,
html body .os-cta-btn:active,html body .oblo-btn:active,html body .ocb-btn:active,
html body .ovou-btn:active,html body .bf-btn:active,html body .opro-btn:active,
html body .os-form-btn:active{transform:translateY(0) scale(.985);
  box-shadow:var(--os-btn-shadow)!important;}
html body .osl-btn:focus-visible,html body .osb-btn:focus-visible,
html body .bk-btn:focus-visible,html body .cv-btn:focus-visible,
html body .sr-btn:focus-visible,html body .os-login-btn:focus-visible,
html body .os-btn:focus-visible,html body .osms-btn:focus-visible,
html body .os-card-btn:focus-visible,html body .os-cta-btn:focus-visible,
html body .oblo-btn:focus-visible,html body .ocb-btn:focus-visible,
html body .ovou-btn:focus-visible,html body .opro-btn:focus-visible,
html body .os-form-btn:focus-visible{outline:3px solid #004214!important;outline-offset:2px!important;}

/* The quiet variant: same geometry, same glass, no fill. Dark Green type inside
   a Green edge -- 11.4:1 on white, where Green-on-white would be 4.9. */
html body .os-btn--ghost,
html body .osl-btn-ghost,
html body .oblo-ghost,html body .ocb-ghost,html body .ovou-ghost{
  background:transparent!important;color:#004214!important;
  border:1px solid #2D803B!important;
  box-shadow:0 4px 14px rgba(0,66,20,.10)!important;}
html body .os-btn--ghost:hover,
html body .osl-btn-ghost:hover,
html body .oblo-ghost:hover,html body .ocb-ghost:hover,html body .ovou-ghost:hover{
  background:#2D803B!important;color:#FFFFFF!important;border-color:#2D803B!important;
  box-shadow:var(--os-btn-shadow-hover)!important;}

/* A ghost button on a DARK ground has to reverse, or it is Dark Green type on
   Dark Green: the "Book at Plattekloof instead" button inside booking's green
   notice went invisible the moment the one recipe landed. Same geometry, same
   glass, inverted ink. */
html body .oblo-notice .oblo-ghost,
html body .oblo-notice .os-btn--ghost,
html body #brackenfell-opening .os-btn--ghost,
html body #brackenfell-opening .bf-btn--ghost,
html body .occ-info .os-btn--ghost,
html body .os-banner .os-btn--ghost{
  color:#FFFFFF!important;border-color:rgba(255,255,255,.55)!important;
  box-shadow:0 4px 14px rgba(0,0,0,.18),inset 0 1px 0 rgba(255,255,255,.22)!important;}
html body .oblo-notice .oblo-ghost:hover,
html body .oblo-notice .os-btn--ghost:hover,
html body #brackenfell-opening .os-btn--ghost:hover,
html body .occ-info .os-btn--ghost:hover,
html body .os-banner .os-btn--ghost:hover{
  background:#FFFFFF!important;color:#004214!important;border-color:#FFFFFF!important;}

html body .os-btn:disabled,html body .os-form-btn:disabled{
  opacity:.55!important;cursor:not-allowed;transform:none!important;
  box-shadow:none!important;}

/* The dashboard's own controls take the vocabulary but keep their size: they sit
   in table rows and toolbars, where a 46px floor would push the data off screen.
   Radius, face and motion only. */
html body .os-v-btn,html body .os-v-btn-view,html body .os-v-btn-resend,
html body .os-v-btn-archive,html body .os-v-btn-restore,
html body .os-approve-btn,html body .os-confirm-btn,html body .os-print-btn,
html body .os-quick-btn,html body .os-redeem-btn,html body .os-refresh-btn,
html body .os-resend-btn,html body .pbtn,html body .vset-nbtn,html body .bk-padbtn{
  border-radius:999px!important;font-family:var(--os-font)!important;
  transition:background-color .18s ease,border-color .18s ease,
             box-shadow .18s ease,transform .18s ease!important;}
html body .os-v-btn:hover,html body .os-v-btn-view:hover,html body .os-v-btn-resend:hover,
html body .os-approve-btn:hover,html body .os-confirm-btn:hover,html body .os-print-btn:hover,
html body .os-quick-btn:hover,html body .os-redeem-btn:hover,html body .os-refresh-btn:hover,
html body .os-resend-btn:hover,html body .pbtn:hover{
  box-shadow:0 4px 14px rgba(0,0,0,.16)!important;transform:translateY(-1px);}

/* Selection controls -- filter chips and the branch radios -- are not buttons,
   so they keep their own size, but they take the same edge, radius and motion
   so the page has one vocabulary rather than two. */
html body .osms-pill,html body .os-branch-opt,html body .os-branch-pick label{
  border-radius:999px!important;font-family:var(--os-font)!important;
  transition:background-color .18s ease,border-color .18s ease,box-shadow .18s ease!important;}
html body .osms-pill:hover,html body .os-branch-opt:hover,html body .os-branch-pick label:hover{
  box-shadow:0 4px 14px rgba(0,66,20,.12)!important;}

@media (prefers-reduced-motion:reduce){
  html body .os-btn,html body .osms-btn,html body .os-card-btn,html body .os-cta-btn,
  html body .oblo-btn,html body .ocb-btn,html body .ovou-btn,html body .opro-btn,
  html body .os-form-btn,html body .osms-pill,html body .os-branch-opt{
    transition:none!important;}
  html body .os-btn:hover,html body .os-form-btn:hover{transform:none;}
}

/* --- Contact: two rows of two --------------------------------------------
 * Branch card | form, then map | contact panel. The four blocks come from three
 * different places in Elementor's tree, so they are re-homed into one grid
 * rather than nested back into columns.
 */
html body .os-contact-grid{display:grid;gap:24px;
  grid-template-columns:repeat(2,minmax(0,1fr));align-items:stretch;
  max-width:1200px;margin:0 auto;padding:0 20px;}
/* minmax(0,1fr) rather than 1fr: a grid item's min-width is auto, so the map
   iframe and the form's long placeholder text would each push their column
   wider than half and break the two-up. */
/* column, not row: the form cell holds the Elementor column AND the moved
   actions, and in row direction they sat side by side -- the form squeezed
   to 308px of a 538px cell with the two buttons floating beside it. */
html body .os-contact-grid > .occ-cell{min-width:0;display:flex;flex-direction:column;}
/* The Contact info panel is gone -- it repeated the branch card beside it -- so
   the map has row two to itself and runs the full width. */
html body .os-contact-grid > .occ-map{grid-column:1 / -1;}
/* Row two has no sibling to take its height from any more, and the earlier
   stretch rule had pinned the iframe to height:100% of nothing -- it collapsed
   to 150px. State the height outright, with the weight to beat that rule. */
html body .os-contact-grid > .occ-map,
html body .os-contact-grid > .occ-map .elementor-element,
html body .os-contact-grid > .occ-map .elementor-widget-container,
html body .os-contact-grid > .occ-map .elementor-custom-embed{min-height:380px;}
html body .os-contact-grid > .occ-map iframe{height:380px!important;min-height:380px!important;}
html body .os-contact-grid > .occ-cell > *{width:100%;}
/* These were Elementor columns sized by percentage width; inside the grid the
   track is the width, so the percentage has to stop competing with it. */
html body .os-contact-grid .elementor-column{width:100%!important;max-width:none!important;}
/* The card's own section painted a full-bleed ground and 56px of padding when
   it ran the width of the page. In a cell it is a card like the others. */
html body .os-contact-grid #os-contact-branches{padding:0!important;background:transparent!important;}
/* .ocb-inner carried `margin:0 auto` too -- same trap one level up, and it is the
   one that was actually holding the card at 348px: the auto margins resolved to
   55px a side. Both boxes have to stop centring before the card can stretch. */
html body .os-contact-grid #os-contact-branches .ocb-inner{
  max-width:none;margin-left:0;margin-right:0;align-self:stretch;}
/* max-width:none was not enough: the card kept `margin:0 auto` from when it was
   a single centred card on a full-width section, and an auto margin on a flex
   item makes it centre at its CONTENT width instead of stretching. It measured
   348px inside a 459px cell, so row one looked inset against row two. */
html body .os-contact-grid #os-contact-branches .ocb-card-solo{
  max-width:none;margin-left:0;margin-right:0;align-self:stretch;}
/* Every box between the cell and the iframe, not just the two nearest: the
   widget-container in the middle kept its intrinsic 300px and pinched the map
   to a third of its column. */
html body .os-contact-grid .occ-map .elementor-widget-container,
html body .os-contact-grid .occ-map .elementor-custom-embed,
html body .os-contact-grid .occ-map iframe{width:100%;height:100%;min-height:320px;
  border:0;border-radius:14px;display:block;}
/* The panel's links were the site's Green on the panel's own Green ground --
   present, legible to a parser, invisible to a reader. */
html body .os-contact-grid .occ-info a{color:#FFFFFF!important;}
html body .os-contact-grid .occ-info a:hover,
html body .os-contact-grid .occ-info a:focus-visible{
  text-decoration:underline!important;text-underline-offset:3px;}
html body .os-contact-grid .occ-map > *{display:flex;}
/* The Green ground belonged to the Contact info panel, but Elementor had put it
   on the outer widget-wrap -- fine when that wrap held only the panel, wrong now
   that it holds all four cells, because the whole grid turned green and the
   panel's own white type had nothing behind it. The ground moves to the cell it
   describes. */
html body .elementor-widget-wrap:has(> .os-contact-grid){background:transparent!important;}
html body .os-contact-grid .occ-info{background:#2D803B;border-radius:14px;overflow:hidden;}
/* Elementor's own column padding on this panel was 80px left and 0 right -- fine
   when it was the page's right-hand column, lopsided as a card, with the
   address running into the edge. Even inset, stated with enough weight to
   beat it. */
html body .os-contact-grid .occ-info .elementor-widget-wrap{padding:30px 28px!important;}

/* Row-to-row matching. The cells already stretch to the row's height, but their
   CONTENTS did not, so the card on the left stopped short of the form beside it
   and the map stopped short of the panel. Every box between the cell and the
   thing you can see has to carry the stretch down. */
/* :not(style):not(script) is load-bearing. The branch card's own <style> block
   is a direct child of its cell, and `display:flex` on a <style> element makes
   it VISIBLE -- the page printed its own CSS as body copy. */
html body .os-contact-grid > .occ-cell > *:not(style):not(script),
html body .os-contact-grid .elementor-column,
html body .os-contact-grid .elementor-widget-wrap{display:flex;flex-direction:column;}
html body .os-contact-grid style,html body .os-contact-grid script{display:none!important;}
html body .os-contact-grid .occ-visit .ocb-inner,
html body .os-contact-grid .occ-visit .ocb-card-solo{display:flex;flex-direction:column;flex:1 1 auto;}
html body .os-contact-grid .occ-info .elementor-widget-wrap{justify-content:flex-start;}
/* The map has no content of its own to stretch, so it takes the height directly
   and loses the min-height that was holding it at a fixed 320. */
html body .os-contact-grid .occ-map .elementor-element,
html body .os-contact-grid .occ-map .elementor-widget-container,
html body .os-contact-grid .occ-map .elementor-custom-embed{flex:1 1 auto;min-height:0;}
html body .os-contact-grid .occ-map iframe{height:100%!important;min-height:0!important;}

/* The branch's two actions now live under the form's submit. */
html body .os-contact-grid .occ-form-actions{margin-top:14px;}
/* Two equal halves rather than shrink-to-fit, so "Reserve on WhatsApp" (193px)
   and "Directions" (115px) stop being different-sized buttons on the same row.
   minmax(0,1fr) because a grid item's min-width is auto and the longer label
   would otherwise still claim more than half. */
html body .os-contact-grid .occ-form-actions .ocb-cta{margin:0;display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;}

/* One height for every control in this column: the two actions measured 42 and
   Send Message 52, which read as three sizes stacked. 46px, set as a min-height
   with the padding taken off the vertical axis, so a wrapped label grows the
   button instead of overflowing it. */
html body .os-contact-grid .occ-form-actions .ocb-btn,
html body .os-contact-grid .occ-form .os-form-btn{
  min-height:46px!important;display:inline-flex!important;
  align-items:center!important;justify-content:center!important;
  padding-top:0!important;padding-bottom:0!important;text-align:center;}
html body .os-contact-grid .occ-form .os-form-btn{width:100%;}
/* The form column kept a 60px right gutter from the old two-column layout, so
   the form measured 427px inside a 487px cell and Send Message sat visibly
   narrower than the two buttons under it. The grid's gap is the gutter now. */
html body .os-contact-grid .occ-form .elementor-widget-wrap{padding-right:0!important;}
/* The buttons' own styling was scoped to #os-contact-branches, which they have
   just left -- outside it they rendered as bare links. Same recipe, now keyed to
   where they live. */
html body .os-contact-grid .occ-form-actions .ocb-btn{display:inline-block;
  background:#2D803B;color:#FFFFFF;text-decoration:none;padding:11px 20px;
  border-radius:999px;font-weight:700;font-size:15px;font-family:var(--os-font);
  transition:background .16s ease;}
html body .os-contact-grid .occ-form-actions .ocb-btn:hover{background:#246a31;}
html body .os-contact-grid .occ-form-actions .ocb-ghost{background:transparent;
  color:#004214;border:1px solid #004214;}
html body .os-contact-grid .occ-form-actions .ocb-ghost:hover{background:#004214;color:#FFFFFF;}
html body .os-contact-grid .occ-form-actions .ocb-btn:focus-visible{
  outline:3px solid #004214;outline-offset:2px;}
@media (prefers-reduced-motion:reduce){
  html body .os-contact-grid .occ-form-actions .ocb-btn{transition:none;}
}

@media (max-width:921px){
  html body .os-contact-grid{grid-template-columns:minmax(0,1fr);gap:20px;}
}

/* The country-code row took as much height as the message box, on a field that
   is optional. Tighten it, and give the message the space -- it is the one field
   the visitor actually has to fill in.
 */
html body .os-contact-grid .occ-form .os-phone-row{margin-bottom:10px;}
html body .os-contact-grid .occ-form .os-phone-row select,
html body .os-contact-grid .occ-form .os-phone-row input{
  padding-top:9px!important;padding-bottom:9px!important;}
html body .os-contact-grid .occ-form textarea{min-height:170px!important;}

/* --- Brochure cards: one baseline for the buttons -------------------------
 * The grid already stretches the cards to equal height, but the button followed
 * the description, so a three-line blurb and a five-line one put their buttons
 * 40px apart -- and the button was left-aligned inside a centred card. The
 * description takes the slack and the button is pushed to the bottom of the
 * card, centred, so the row of buttons reads as one line whatever the copy does.
 */
html body .os-card{display:flex!important;flex-direction:column!important;}
html body .os-card-body{display:flex!important;flex-direction:column!important;
  flex:1 1 auto!important;text-align:center!important;}
html body .os-card-desc{flex:1 1 auto!important;}
html body .os-card-btn{align-self:center!important;margin-top:auto!important;}

/* --- Links: no rules under the words ------------------------------------
 * Every content link carried a green underline — addresses, phone numbers,
 * emails, "View the menu" — which on a page that is mostly contact details reads
 * as a page full of ruled lines. Colour and hover carry the affordance instead,
 * and the underline comes back on hover so a link is still discoverable.
 */
html body #os-locations .osl-fact a,html body .osl-fact a,
html body .osl-panel a,html body .osl-facts a,html body #os-locations a,
html body .os-branchnote a,html body .entry-content a,html body .elementor-widget-text-editor a{
  text-decoration:none!important;}
html body .osl-panel a:hover,html body .osl-facts a:hover,html body #os-locations a:hover,
html body .os-branchnote a:hover,html body .entry-content a:hover,
html body .elementor-widget-text-editor a:hover{text-decoration:underline;}

/* --- Footer policy links ------------------------------------------------
 * Appointment Times, Cancellation & Refunds, Voucher Terms and Health & Safety
 * were linked from the HOME PAGE ONLY. Trimming the home page to a one-screen
 * chooser would have orphaned four live pages — reachable by typing the URL and
 * no other way. They belong in the footer, which is on every page, and they
 * should have been there all along.
 */
html body .os-footer-links{display:flex;flex-wrap:wrap;justify-content:center;
  gap:6px 20px;margin:0 0 10px;font-size:13px;line-height:1.6;}
html body .os-footer-links a{color:var(--os-dark-green);text-decoration:none;
  border-bottom:1px solid transparent;}
html body .os-footer-links a:hover,html body .os-footer-links a:focus-visible{
  color:var(--os-green);border-bottom-color:currentColor;}

/* --- Type --- */
body,button,input,select,optgroup,textarea{font-family:var(--os-font);}
h1,h2,h3,h4,h5,h6,.elementor-heading-title,.site-title{font-family:var(--os-font);}

