* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* Mandy's purple palette. These are the values the site has always used; they
   are named here so the type, buttons, forms and cards can share them instead
   of repeating raw hex.

   Two jobs are done with colour on this site and they need different
   treatment. The purple is the brand and the page frame, so it carries the
   header, the navigation and the background. Body copy is dark ink on a light
   surface instead, because white on mid-purple measured about 2.9:1 and the
   About page is mostly long paragraphs. Anything you can read for more than a
   few seconds now sits on --surface, which clears WCAG AA comfortably. */
:root {
    --purple-900: #3c2482;
    --purple-800: #4a30b0;
    --purple-700: #5a35c8;
    --purple-600: #8080ff;
    --purple-500: #9b51e0;
    --purple-300: #b088d8;
    --purple-200: #ccccff;
    --purple-100: #eeddff;

    /* Surfaces are inverted from the original scheme: the page block carries
       the light mauve, and the section cards on top of it are white. */
    --surface: #f7f5ff;
    --surface-sunk: #ffffff;
    /* The two surfaces are only 1.08:1 apart, so edges need a real border to
       stay visible. --purple-200 was too faint once white became the card. */
    --border: #b088d8;
    --border-field: #9480d8;
    --page-top: #9b8cf5;
    --page-bottom: #7b6ae8;

    --ink: #2b2540;
    --ink-soft: #554d6b;
    --white: #ffffff;

    --yellow: #eeee22;

    --shadow-sm: 0 1px 3px rgba(74, 48, 176, 0.16);
    --shadow-md: 0 8px 24px rgba(74, 48, 176, 0.16);
    --shadow-lg: 0 18px 44px rgba(58, 36, 122, 0.24);

    --radius-sm: 8px;
    --radius: 14px;
    --radius-lg: 22px;

    /* Display: a warm humanist sans rather than a serif. Optima and Avenir
       Next lead where they exist (macOS, iOS), Trebuchet MS covers Windows,
       and the sans-serif tail catches everything else. Georgia is the last
       resort, kept so the stack never falls to Times. */
    --font-display: Optima, Candara, 'Avenir Next', Avenir, 'Trebuchet MS',
        'Segoe UI', Verdana, Georgia, sans-serif;
    /* Body: unchanged. The platform UI face is the most legible thing
       available and the paragraphs here are long, so the warmth belongs in
       the display face only. */
    --font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
        'Helvetica Neue', Arial, sans-serif;

    --measure: 68ch;
}

html {
    scroll-behavior: smooth;
    /* iOS Safari's text autosizing, which inflates type in wide blocks on a narrow
       viewport and was how the ABN lost its number on a phone. 100% switches it
       off, and the viewport meta already allows user-scalable zoom, so no one loses
       the ability to enlarge the text. Worth keeping now the ABN has moved: the
       same boosting can misrender any fixed-size text in a narrow column. */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

body {
    font-family: var(--font-body);
    font-size: 17px;
    line-height: 1.65;
    color: var(--ink);
    /* The purple page frame is gone. That gradient could not carry body text -
       dark ink measured about 3.5:1 at the bottom of it and light text 2.6:1 -
       and with the outer panel and the cards both removed there is nothing left
       between the copy and the background. Purple now lives in the nav bar and
       the footer, and the page itself is the light surface. */
    background-color: var(--surface);
    -webkit-font-smoothing: antialiased;
}

a {
    color: var(--purple-800);
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
}

a:hover {
    color: var(--purple-900);
    text-decoration-thickness: 2px;
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
iframe:focus-visible {
    outline: 3px solid var(--purple-900);
    outline-offset: 3px;
    border-radius: 3px;
}

/* Headings are the display face. The old site made every heading bold
   italic from one global rule, which is why the markup was full of <em> tags
   nested inside already-italic headings. Weight and slant are set here once
   instead, so the markup can stay clean. */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-display);
    font-style: normal;
    font-weight: 700;
    line-height: 1.2;
    color: var(--purple-900);
    text-wrap: balance;
}

.container {
    max-width: 1140px;
    margin: 0 auto;
    padding: 0 20px;
}

/* ---------------------------------------------------------------- Header --- */

.site-header {
    width: 100%;
    position: relative;
}

.header-top {
    background-color: var(--purple-800);
    padding: 12px 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    min-height: 64px;
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.12);
}

/* Just the studio name now. The ABN used to sit under it, which is why this was a
   column flex with a 2px gap; that has moved to the footer, where nothing has to
   share the row with a menu button. */
.site-branding {
    min-width: 0;
}

.site-title {
    font-family: var(--font-display);
    font-size: 19px;
    line-height: 1.25;
    margin: 0;
}

/* The ABN, in the footer under the copyright. Verified against the ABR register:
   CUMMINS, MANDY, Individual/Sole Trader, active from 13 Feb 2006, VIC.

   It was in the header until this move, and the nowrap that used to sit here is
   gone with it. In the header this was a flex item in a shrinkable column sharing
   a row with the menu button, so nowrap could not hold its width - it only let
   the text spill out of a box too small for it, where the button, painted later as
   a sibling, covered the end of the line. That is the "ABN:" and no number that
   iPhone portrait was showing. Nothing about that applies here: the footer wraps,
   and the number is kept in one piece by the non-breaking spaces in the markup,
   so the only place it can break is after the label.

   White at 75% was 6.4:1 on the footer's --purple-900, over the 4.5:1 that 12px
   text needs, but it read as a different, greyer colour than the copyright line
   above it. It is now the same --purple-200 the copyright uses, solid rather than
   faded, so the two read as one block; the size difference still separates them.
   The number is a link out to the ABR register, so the anchor has to be pulled
   back to that colour by hand - the global `a` is --purple-800, which is close
   enough to the footer's own background to disappear.

   The margin is 0 because landscape puts it beside the copyright on one
   baseline; the stacked portrait layout sets its own gap from the flex row. */
.site-abn {
    font-family: var(--font-body);
    font-size: 12px;
    line-height: 1.2;
    color: var(--purple-200);
    margin: 0;
}

/* Inherit, so the link is the same colour as the "ABN:" label beside it. */
.site-abn a {
    color: inherit;
}

/* Hover keeps the underline and the global 2px thickening, and only lifts the
   colour to white. The global a:hover is --purple-900, which is the footer's
   own background. */
.site-abn a:hover {
    color: var(--white);
}

.site-title a {
    color: var(--white);
    text-decoration: none;
}

.site-title a:hover {
    color: var(--purple-100);
    text-decoration: none;
}

.desktop-nav {
    display: flex;
    align-items: center;
    list-style: none;
    gap: 2px;
}

.desktop-nav li a {
    color: var(--white);
    padding: 10px 14px;
    display: block;
    border-radius: 999px;
    text-decoration: none;
    transition: background-color 0.2s ease;
    white-space: nowrap;
}

.desktop-nav li a:hover {
    background-color: rgba(255, 255, 255, 0.18);
    text-decoration: none;
}

.desktop-nav li.active a {
    background-color: rgba(255, 255, 255, 0.26);
}

/* The hero: one full-bleed band, text beside the photo rather than a text box
   floating on a full-bleed photo.

   That swap is what the old layout got wrong. The photo is square and the old
   band was 3.4:1, so cover-cropping it to the band threw away about 70% of the
   picture and upscaled the 640px source to 2.25x to fill the width - a soft
   fragment of it, with a solid panel pasted on top. As a column of its own the
   photo is cropped far less and sits nearer its own size.

   The band runs the full width of the viewport with no margin above or beside
   it, so it butts straight up against the purple nav bar. .banner, the wrapper
   around the card, therefore has no styles at all: a width cap, side padding or
   top margin on it is exactly what has to be absent. The space below the band
   is .site-content's top padding, which is what separates it from the panel of
   sections underneath. */
.banner-card {
    display: grid;
    /* 45/55, up from 50/50. Expressed as fr rather than a percentage so the
       split holds at any width, and so there is no rounding gap between the two
       tracks at awkward viewport sizes.

       The cost is in the text column: 45% of 1440px less its padding leaves
       about 570px, which at the 56px headline fits "Learn Piano the Fast" and no
       more, so the title can run to three lines. The row is sized by the text
       column, so that makes the whole banner taller rather than clipping it. */
    grid-template-columns: 9fr 11fr;
    /* A floor under the height, about 50% more than the band was at 36px type
       and 32px of padding. The text column is what normally sets the height, so
       without this the banner collapses to a single line of headline on a wide
       monitor. The extra space is absorbed as padding either side of the
       centred text, and the photo fills it. */
    min-height: clamp(200px, 17vw, 260px);
    /* A flat --purple-800, the same fill as the .header-top bar directly above,
       so the two bands meet with no seam at all. There was a vertical gradient
       here first, from this colour down to #2b1a63; it is gone, and the border
       below went with it. A gradient cannot start on the bar's colour and stay
       matched along the whole of that edge unless it holds the first stop for a
       seventh of its height, and once it does, it is nearly a flat fill for most
       of its length anyway.

       Not a repeat of the page gradient that was removed in the surfaces commit:
       that was mid purple, 2.6:1 to 3.5:1 against this text. This is the dark end
       of the palette, where white measures 11.9:1. */
    background-color: var(--purple-800);
    /* Sits under the photo as well as the panel, since the card is the box both
       are in. Full bleed, so this is the banner's only edge on the light page. */
    box-shadow: var(--shadow-md);
    /* 20px, and the same colour as the fill, so it reads as the banner simply
       being that much taller rather than as a band under it. border-box, set on
       the reset, means the 20px comes out of the card's own height rather than
       being added to it, so the banner does not grow; the min-height floor above
       now has 20px less room inside it for the text. */
    border-bottom: 20px solid var(--purple-800);
}

.banner-body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* Up from clamp(20px, 2.6vw, 32px), which was the height cut made when the
       panel was full width and the text ran to one line. */
    padding: clamp(24px, 3.2vw, 40px);
    text-align: left;
}

/* A wrapper, not the img itself, and that is the whole point of it.

   As a grid item the img's height: 100% resolves against a row that is still
   being sized, so the browser falls back to its intrinsic ratio and the row is
   sized to the photo - a square source at half the viewport width made the
   banner 720px tall at 1440px. Taking the img out of flow entirely means the
   row is sized by the text column alone and the photo simply fills whatever
   height that turns out to be. */
.banner-photo {
    position: relative;
    /* Clips the image to the column, so the overhang cannot spill past it. */
    overflow: hidden;
    /* The radius goes on the wrapper, not the img. The img is absolutely
       positioned and inset: 0, so it fills the wrapper's padding box edge to
       edge; rounding it would round a box that is the same size as its parent and
       clip nothing that overflow: hidden was not already clipping. On the
       wrapper, the two together do what they look like they do - the curve is cut
       out of the image.

       All four corners, so the purple shows at the corners of the band. That is
       deliberate, and it is why the wrapper is rounded rather than the card: the
       card is full bleed, and a radius on it would cut the banner away from the
       edges of the screen. */
    border-radius: var(--radius);
    /* 20px of clear purple down the right-hand side, as a margin rather than a
       border. The two look almost the same - the card behind is the same purple -
       but they are not: a border is drawn along the border box, which the radius
       curves, so a bordered right edge gets cut short at the top and bottom
       corners. A margin is outside the box, so the image is inset whole, all four
       corners are really rounded, and the strip runs to the screen edge.

       The wrapper is a grid item, so the margin eats into its 11fr track rather
       than pushing the photo along: the grid column is unchanged and the image
       gives up the 20px. The img inside is inset: 0 and needs nothing - it fills
       whatever box the wrapper ends up with. */
    margin-right: 20px;
}

.banner-photo img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* The file itself is never rescaled, cropped or re-encoded - the browser
       draws it at whatever size the column is. cover fills the column and
       object-position centres the image in it, so a square photo in a shorter
       panel keeps its proportions, the overhang is split evenly top and bottom,
       and the wrapper's overflow: hidden clips it. */
    object-fit: cover;
    object-position: center center;
}

.banner-title {
    font-family: var(--font-display);
    font-weight: 700;
    font-style: italic;
    /* White, because the panel is now the dark end of the palette. 11.9:1 on
       --purple-800, and more than that further down the gradient. */
    color: var(--white);
    /* Up from clamp(32px, 4.4vw, 56px), and from 44px before that. At 68px in
       the 45% text column the headline still breaks in two on a wide monitor -
       "Learn Piano the" over "Fast and Fun Way" - and text-wrap: balance keeps
       the two lines even. The 2px keyline is a thinner proportion of the letter
       again, so it reads lighter still; drop it to 1.5px before going up further,
       since past that the stroke starts closing the counters in "a". */
    font-size: clamp(34px, 5vw, 68px);
    line-height: 1.08;
    /* Tightened at this size. Display type wants negative tracking; at 28px on a
       phone it closes the words up slightly without the letters touching. */
    letter-spacing: -0.015em;
    text-wrap: balance;
    margin: 0 0 12px;
}

/* The title is filled with a near-white gradient and outlined in a mid tone.

   The outline has to be lighter than the panel and darker than the fill, or there
   is nothing to see. Three arrangements failed that test: a translucent
   --purple-100, which is the same hue and nearly the same lightness as the fill's
   own light end and read as nothing at all; #2b1a63, which was the panel
   gradient's darkest stop while there was a gradient, 14.6:1 against the fill but
   only about 1.2:1 against the panel by the time the gradient reached that
   colour, so the keyline was strong at the top of the banner and gone at the
   bottom; and the two swapped round, a --purple-300 body inside a white ring,
   which satisfies both contrasts only in a narrow band and leaves the ring the
   weaker of the pair. --purple-300 as the stroke and near-white as the body is
   the version where both hold at once - 2.9:1 against the fill and 3.2:1 against
   the panel. A flat panel suits that better than the gradient did: the ring's
   contrast against the background no longer varies with height.

   1px, down from 2px. The stroke is drawn centred on the glyph edge with no
   paint-order to control it, so at 1px half of it is inside the letter and the
   half that shows is 0.5px - a true hairline, and on a non-Retina display that
   may round away to nothing. It is opaque rather than 90% for that reason: with
   this little of the ring visible, the alpha is the only lever left to make it
   read. If it disappears at 1x, 1.5px is the next step up. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .banner-title {
        background-image: linear-gradient(115deg, #ffffff 0%, #fbf7ff 55%, #f0e6ff 100%);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
        -webkit-text-fill-color: transparent;
        -webkit-text-stroke: 1px #b088d8;
    }
}

.banner-subline {
    font-family: var(--font-body);
    font-weight: 400;
    /* Was --ink-soft, for the pale panel this no longer is. --purple-100 gives a
       second tone against the white heading, and 11.5:1 on the panel's darkest
       stop. */
    color: var(--purple-100);
    /* Up from clamp(17px, 1.6vw, 21px) to match the larger headline. The
       original clamp(24px, 2.9vw, 34px) was sized to sit under a full-width
       headline and read as a second headline of its own. */
    font-size: clamp(19px, 1.8vw, 24px);
    line-height: 1.45;
    max-width: 34ch;
    margin: 0;
}

/* ------------------------------------------------------- Mobile menu --- */

.mobile-menu-toggle {
    display: none;
    background: none;
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: var(--radius-sm);
    cursor: pointer;
    padding: 9px 10px;
}

.hamburger {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.hamburger span {
    display: block;
    width: 24px;
    height: 3px;
    background-color: var(--white);
    border-radius: 2px;
    transition: transform 0.3s;
}

.header-menu-sidebar {
    display: none;
    position: fixed;
    top: 0;
    right: -300px;
    width: 300px;
    height: 100%;
    background-color: var(--white);
    z-index: 1000;
    transition: right 0.3s ease;
    box-shadow: var(--shadow-lg);
}

.header-menu-sidebar.active {
    right: 0;
}

.header-menu-sidebar .close-btn {
    position: absolute;
    top: 12px;
    right: 14px;
    background: none;
    border: none;
    font-size: 28px;
    line-height: 1;
    cursor: pointer;
    color: var(--ink);
}

.sidebar-nav {
    list-style: none;
    padding: 60px 22px 20px;
}

.sidebar-nav li a {
    display: block;
    padding: 13px 0;
    color: var(--ink);
    border-bottom: 1px solid #eceaf6;
    font-size: 17px;
    text-decoration: none;
}

.sidebar-nav li a:hover {
    color: var(--purple-800);
    text-decoration: none;
}

.sidebar-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(30, 20, 60, 0.55);
    z-index: 999;
}

.sidebar-overlay.active {
    display: block;
}

/* ------------------------------------------------------------ Content --- */

.site-content {
    margin: 0;
    /* Top padding cut from clamp(28px, 5vw, 56px). It stacks with the banner's
       own bottom padding, so the gap between the hero and the panel was 92px on
       a desktop; it is 72px now. */
    padding: clamp(20px, 3vw, 36px) 20px;
}

/* The outer panel that used to be .content-area is gone - no background, no
   radius, no shadow, no padding of its own. <main> is kept for the landmark; the
   page is divided by rules instead, below. */
.content-inner {
    /* 1040px down to 960px. The column was as wide as the old panel minus its
       padding, and the panel no longer exists; 960px keeps the three-up row and
       the feature grid comfortable at any width while the prose inside stays on
       --measure, and .container's 20px a side handles a phone. */
    max-width: 960px;
    margin: 0 auto;
}

/* Sections separated by a hairline rather than boxed.

   It hangs off .content-inner's children rather than off .section, so it applies
   to whatever a page's top-level blocks happen to be - .section, .reason,
   .contact-layout, .video-columns - with no per-page divider markup, and the
   first block never gets a rule above it. */
.content-inner > * + * {
    margin-top: clamp(32px, 4.5vw, 52px);
    padding-top: clamp(28px, 4vw, 40px);
    border-top: 1px solid var(--border);
}

/* ...except straight after the breadcrumb trail. The trail is a navigation aid
   sitting at the top of the page rather than a section of its own, so a hairline
   and 92px of stacked margin and padding above the lead paragraph read as a
   section break that isn't one. Its own margin-bottom is the only gap left. */
.content-inner > .breadcrumbs + * {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

/* A page may ask for a rule above its very first block by putting .ruled-top on
   it, which carries the same spacing and colour as the system above. No page does
   at the moment: the home page had it, until the banner grew a 20px band along
   the bottom to divide on. That band is now the same colour as the banner, so it
   is not dividing anything - the only thing between the banner and the lead
   section is --shadow-md. If the page wants its edge back, putting this class on
   the lead card is the change; it is four lines to keep for that. */
.ruled-top {
    margin-top: clamp(32px, 4.5vw, 52px);
    padding-top: clamp(28px, 4vw, 40px);
    border-top: 1px solid var(--border);
}

/* The other half of that pair: .unruled-top on a block that follows another one
   as a continuation of it rather than as a section of its own, so the hairline
   .content-inner > * + * would otherwise draw is suppressed. Zeroing margin-top
   is safe because the block above carries its own bottom margin, which is the
   gap that ends up between the two. */
.unruled-top {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

/* A lead paragraph under the page title. */
.page-intro {
    font-size: clamp(18px, 1.6vw, 21px);
    color: var(--ink-soft);
    /* Was max-width: var(--measure), which left the lead paragraph a third
       narrower than the column it opens, and the rule above it full width. */
    margin-bottom: clamp(28px, 4vw, 40px);
}

/* Home link at the top of the inner pages, which no longer carry the banner, so
   there is nothing above them to click back to. A plain trail, not a heading:
   the current page is plain text marked with aria-current rather than an h1. */
.breadcrumbs {
    /* Now the only gap between the trail and the paragraph under it. */
    margin-bottom: 20px;
}

.breadcrumbs ol {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    /* 8px up to 10px with the type enlarged, so the crumbs and the separator
       between them are not crowding each other. */
    gap: 10px;
    /* 15px, then 30px, then back 10% to 27px. */
    font-size: 27px;
}

.breadcrumbs li {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--ink-soft);
}

/* Drawn between the crumbs rather than after each one, so there is no trailing
   separator hanging off the end of the trail. */
.breadcrumbs li + li::before {
    content: '/';
    color: var(--purple-300);
}

.breadcrumbs a {
    color: var(--purple-800);
    font-weight: 600;
    text-decoration: none;
}

.breadcrumbs a:hover,
.breadcrumbs a:focus-visible {
    text-decoration: underline;
}

.breadcrumbs [aria-current="page"] {
    color: var(--ink);
}

/* -------------------------------------------------------------- Cards --- */

/* Not a card in the box sense any more: no background, no border, no radius, no
   shadow, no hover. The class stays because it still carries the body
   typography, the float clearing below, and the float margins for the photos. */
.card {
    color: var(--ink);
    font-size: 17px;
    line-height: 1.7;
    text-align: left;
}

.card::after {
    content: '';
    display: table;
    clear: both;
}

.card img {
    border-radius: var(--radius);
}

.card img.float-left {
    float: left;
    margin: 4px 20px 8px 0;
}

.card img.float-right {
    float: right;
    margin: 4px 0 8px 20px;
}

/* The block that leads a section, with a heading of its own.

   Two columns rather than a floated photo. The photo used to float left and
   the paragraphs wrapped around it, so how much text fitted beside the image
   varied and the sign-off sometimes ended up in the narrow column. As a grid
   the photo gets column one and .about-body gets column two, so every line of
   text - the paragraphs, the sign-off and the button alike - shares one left
   edge. minmax(0, 1fr) on the text column so it can shrink instead of forcing
   the grid wider than its column. */
.card--feature {
    display: grid;
    grid-template-columns: 340px minmax(0, 1fr);
    gap: 30px;
    align-items: start;
}

/* The three reasons on the home page, each a full-width section. The mark sits in
   a fixed column beside the copy rather than above it: the copy keeps the whole
   width of the content column, which is the point of that column being 960px,
   and the section still reads as a section rather than as a paragraph with a logo
   floating in it.

   The wrapper div inside is unstyled and does the same job as one - it keeps the
   heading and the copy in a single grid cell, so the number of paragraphs in a
   section does not change the track layout. */
.reason {
    display: grid;
    grid-template-columns: 72px 1fr;
    column-gap: clamp(16px, 2vw, 28px);
    align-items: start;
}

/* A logo or graphic beside the copy: no box, contained rather than stretched, so
   the two 1:1 brand logos and the 1.87:1 devices graphic all sit in the same box
   without distortion.

   Height 58px is the height the icons had in the three-up row. box-sizing is
   border-box, so the 8px padding and 1px border of the box that used to be around
   them sat inside that 76px and the images themselves were never taller.

   object-position is left rather than centre, which it had to be when the icon
   was a full-width strip across a narrow card. The box is now a column at the
   edge of the section, and a mark centred in it looks adrift. */
.reason-icon {
    width: 72px;
    height: 58px;
    object-fit: contain;
    object-position: left center;
}

.reason-title {
    font-family: var(--font-display);
    font-size: clamp(22px, 2vw, 26px);
    line-height: 1.25;
    color: var(--purple-900);
    margin: 0 0 8px;
}

.reason p {
    margin: 0;
}

.reason p + p {
    margin-top: 0.7em;
}

/* --------------------------------------------------------------- Hero --- */

.hero-card {
    /* No max-width: the box runs the full width of the content column, edge to
       edge with the card grid and sections below it.

       margin-bottom only, so a .ruled-top on this element would be free to set
       the margin-top. A 0 there would win on source order and cancel it. */
    margin-bottom: 28px;
    text-align: left;
}

/* height: auto matters: the width attribute is a hint, but a height attribute
   would otherwise be applied literally and stretch the photo out of shape.

   The width is pinned at 256px - what the photo actually rendered at before
   the card was widened - so widening the card widens the box without
   magnifying the photo. Float it left so the heading and subhead wrap
   around it; .card's ::after clearfix contains the float. */
.hero-card img {
    display: block;
    width: 256px;
    /* Stays 256px wherever that fits; only shrinks on a phone too narrow to
       hold it, which would otherwise overflow the card. */
    max-width: 100%;
    height: auto;
    padding: 0;
    margin: 2px 26px 16px 0;
    border-radius: var(--radius);
    float: left;
}

.hero-heading {
    font-family: var(--font-display);
    /* Slightly down from 40px: the text now shares the row with the photo, so
       it has a narrower column to sit in. */
    font-size: clamp(26px, 3.2vw, 36px);
    color: var(--purple-900);
    line-height: 1.15;
    margin-bottom: 10px;
    /* Wraps in the narrower column, so even out the ragging. */
    text-wrap: balance;
}

.hero-sub {
    color: var(--ink-soft);
    font-size: clamp(16px, 1.5vw, 19px);
    margin: 0;
    text-wrap: pretty;
}

/* The lead paragraph, moved inside the hero card so it wraps around the
   photo along with the heading. */
.hero-copy {
    color: var(--ink-soft);
    font-size: clamp(16px, 1.5vw, 18px);
    line-height: 1.65;
    margin: 12px 0 0;
    text-wrap: pretty;
}

/* The Chinese line closing the hero card is centred across the column,
   separate from the paragraphs above it, and a fifth larger to stand on its
   own. Scoped to the hero so the base rule stays left aligned elsewhere. */
.hero-card .chinese-text {
    text-align: center;
    font-size: clamp(20.4px, 2.16vw, 24px);
    margin-top: 12px;
}

/* Below this the column beside the photo gets too narrow to read, so go back
   to the photo above centred text. The photo stays 256px either way, and the
   box still runs the full width of the column. */
@media screen and (max-width: 860px) {
    .hero-card {
        text-align: center;
    }

    /* Photo above, text below, once the column beside it gets too narrow. */
    .card--feature {
        grid-template-columns: 1fr;
        gap: 20px;
    }

    .hero-card img {
        float: none;
        margin: 0 auto 16px;
    }

    .hero-heading {
        text-wrap: balance;
    }
}

/* ---------------------------------------------------------- Sections --- */

/* .section has no rule of its own any more. The hairline above it, and the
   space around it, come from .content-inner > * + * - which also covers the
   blocks on a page that are not .section, so there is one divider system rather
   than two that have to agree. */

/* Replaces the old <hr>-as-section-divider. */
.section-head {
    text-align: center;
    margin-bottom: 22px;
}

.section-head h2 {
    font-size: clamp(24px, 2.6vw, 32px);
    margin-bottom: 10px;
}

.section-head p {
    color: var(--ink-soft);
    max-width: 60ch;
    margin: 0 auto;
}

.section-icon {
    display: block;
    margin: 0 auto 14px;
    width: 88px;
    height: 88px;
    object-fit: contain;
}

.section-text {
    font-size: 17px;
    line-height: 1.75;
    max-width: var(--measure);
    margin: 0 auto;
}

/* The closing pill, which carries the reader on to the next page (Home to
   About, About to Videos, Videos to Contact). It is a direct child of
   .card--feature rather than sitting inside .about-body, so that grid-column
   can pull it across both columns and centre it on the whole panel. Left
   inside the text wrapper, text-align: centre would only have centred it on
   the text column. */
.closing-cta {
    grid-column: 1 / -1;
    text-align: center;
    margin: clamp(24px, 3.5vw, 36px) 0 0;
}

.text-center {
    text-align: center;
}

/* A pull quote, used for the Simply Music endorsement. */
.pull-quote {
    font-family: var(--font-display);
    font-style: italic;
    font-size: clamp(19px, 2vw, 24px);
    line-height: 1.45;
    color: var(--purple-900);
    text-align: center;
    max-width: 28ch;
    margin: 0 auto 22px;
    padding: 22px 26px;
    background-color: var(--surface-sunk);
    border-radius: var(--radius);
    border: 1px solid var(--border);
}

/* Long-form prose, as on the About page.

   The max-width: var(--measure) that used to be here is gone, and with it the
   rule above this block went short as well - .prose is the <section> itself, so
   capping it capped the hairline the .content-inner > * + * divider draws on
   top of it. Both now run the full width of the content column, which is what
   the page is meant to look like. Note the trade: 960px of 17px body text is
   nearer 100 characters a line than the 45-75 that reads comfortably. If that
   proves hard to read, narrowing .content-inner is the better fix than capping
   the prose again, because it moves the rules with the text. */
.prose p {
    margin-bottom: 1.1em;
}

/* The only list in the body copy, on the About page's first-year summary. Left
   with the browser's own disc and indent rather than a custom marker, so it
   reads as an aside to the argument rather than as another component. */
.prose ul {
    margin: 0 0 1.1em;
    padding-left: 1.25em;
}

.prose li {
    margin-bottom: 0.45em;
}

.prose h2 {
    font-size: clamp(22px, 2.4vw, 28px);
    margin: 1.8em 0 0.6em;
}

.prose h2:first-child {
    margin-top: 0;
}

.prose ul {
    margin: 0 0 1.1em 1.2em;
}

.prose li {
    margin-bottom: 0.4em;
}

.about-image {
    /* No float, and no right margin: the grid column and gap place it. */
    width: 100%;
    height: auto;
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
}

.about-text {
    font-size: 17px;
    line-height: 1.75;
}

.signature {
    font-family: var(--font-display);
    font-style: italic;
    font-size: 19px;
    color: var(--purple-900);
    margin-top: 18px;
}

.signature span {
    /* display: block is what puts the credential on its own line, directly
       under the name, rather than trailing along beside it. */
    display: block;
    font-family: var(--font-body);
    font-style: normal;
    font-size: 14px;
    color: var(--ink-soft);
    margin-top: 4px;
}

/* The Chinese line beneath a block of English copy. It is left aligned by
   default; .hero-card centres and enlarges its own, so keep the base rule
   minimal for anywhere else it gets used. */
.chinese-text {
    text-align: left;
    font-size: clamp(17px, 1.8vw, 20px);
    color: var(--purple-900);
    margin: 4px 0 0;
}

/* ------------------------------------------------------------- Videos --- */

.video-columns {
    display: grid;
    /* min() keeps a single column from forcing a 300px track wider than a
       narrow phone, which is what the old flex min-width did. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
    gap: 24px;
}

.video-column {
    min-width: 0;
}

.video-column iframe,
.narrow-video iframe {
    width: 100%;
    aspect-ratio: 16 / 9;
    border: none;
    border-radius: var(--radius);
    margin-bottom: 14px;
    background-color: var(--surface-sunk);
    box-shadow: var(--shadow-sm);
}

.video-section-title {
    text-align: center;
    margin-bottom: 18px;
}

.video-section-title h2,
.video-column h2 {
    font-size: 19px;
    color: var(--purple-900);
    margin-bottom: 12px;
}

.narrow-video {
    max-width: 620px;
    margin: 0 auto 20px;
}

.video-center {
    text-align: center;
}

.video-center h2 {
    font-size: clamp(22px, 2.4vw, 30px);
    margin-bottom: 18px;
}

/* ------------------------------------------------------------ Buttons --- */
/*  Pill radius on everything, a solid primary against a tinted outline, and a
    shared transition order so the hover feels the same everywhere.

    .btn and these variants are also what the admin inbox renders with, so they
    are not restyled to the public palette below. */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 26px;
    border-radius: 999px;
    font-family: var(--font-body);
    font-size: 16px;
    font-weight: 700;
    letter-spacing: 0.01em;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    transition: background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease,
        transform 0.15s ease, border-color 0.2s ease;
    border: 1px solid transparent;
}

.btn:active {
    transform: translateY(1px);
}

.btn--primary {
    background: var(--purple-700);
    color: var(--white);
    box-shadow: var(--shadow-sm);
}

.btn--primary:hover {
    background: var(--purple-800);
    color: var(--white);
    box-shadow: var(--shadow-md);
    text-decoration: none;
}

.btn--outline {
    background: transparent;
    color: var(--purple-800);
    border-color: var(--purple-300);
}

.btn--outline:hover {
    background: var(--purple-800);
    border-color: var(--purple-800);
    color: var(--white);
    text-decoration: none;
}

/* On the purple header the pairing inverts: the call button goes solid white
   so it reads as the primary action, the text button stays tinted. */
.btn--on-dark {
    background: rgba(255, 255, 255, 0.22);
    color: var(--white);
}

.btn--on-dark:hover {
    background: rgba(255, 255, 255, 0.34);
    color: var(--white);
    text-decoration: none;
}

.btn--on-dark-solid {
    background: var(--white);
    color: var(--purple-800);
}

.btn--on-dark-solid:hover {
    background: var(--purple-100);
    color: var(--purple-800);
    text-decoration: none;
}

.btn--large {
    padding: 15px 34px;
    font-size: 17px;
}

.btn--small {
    padding: 9px 18px;
    font-size: 14px;
}

.btn--full {
    width: 100%;
}

.btn[disabled],
.btn--pending {
    opacity: 0.6;
    cursor: default;
    pointer-events: none;
}

/* The nav pills sit inside .desktop-nav li a and .sidebar-nav li a, whose rules
   are more specific than a bare .btn and would otherwise win on padding, radius
   and colour - leaving a white "Call me" pill white text on a white pill. These
   selectors match their specificity so the button styling survives. */
.desktop-nav li a.nav-sms,
.desktop-nav li a.nav-call,
.sidebar-nav li a.nav-sms,
.sidebar-nav li a.nav-call {
    display: inline-flex;
    align-items: center;
    padding: 9px 18px;
    border-radius: 999px;
    border-bottom: none;
    font-size: 14px;
    font-weight: 700;
    white-space: nowrap;
}

.desktop-nav li a.nav-sms {
    color: var(--white);
}

.desktop-nav li a.nav-call {
    color: var(--purple-800);
}

.sidebar-nav li a.nav-sms {
    color: var(--purple-800);
}

/* The sidebar's "Call me" is .btn--primary - a solid --purple-700 pill - where
   the desktop one is .btn--on-dark-solid, a white pill. The selector above has to
   match .sidebar-nav li a's specificity to stop that winning, so the colour has
   to be restated here as well, and the desktop value is the wrong one to copy: on
   a purple pill, purple text is invisible. The hover was already right. */
.sidebar-nav li a.nav-call {
    color: var(--white);
}

.desktop-nav li a.nav-sms:hover,
.desktop-nav li a.nav-call:hover {
    text-decoration: none;
}

.desktop-nav li a.nav-sms:hover {
    background: rgba(255, 255, 255, 0.34);
    color: var(--white);
}

.desktop-nav li a.nav-call:hover {
    background: var(--purple-100);
    color: var(--purple-800);
}

.sidebar-nav li a.nav-sms:hover {
    background: var(--purple-100);
    color: var(--purple-800);
}

.sidebar-nav li a.nav-call:hover {
    background: var(--purple-800);
    color: var(--white);
}

/* ------------------------------------------------------------- Forms --- */
/*  .form-group, .form-input and .form-label are shared with the admin inbox,
    which renders its login form against this same stylesheet. */

.form-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.form-label {
    font-weight: 700;
    font-size: 15px;
    color: var(--ink);
}

.form-input {
    width: 100%;
    padding: 12px 14px;
    border: 1px solid var(--border-field);
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: 16px;
    color: var(--ink);
    /* Tinted, not white: the panel behind these is now white. */
    background-color: var(--surface);
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.form-input:hover {
    border-color: var(--purple-300);
}

.form-input:focus {
    outline: none;
    border-color: var(--purple-600);
    box-shadow: 0 0 0 3px rgba(128, 128, 255, 0.28);
}

.form-textarea {
    width: 100%;
    padding: 12px 14px;
    border: 1px solid var(--border-field);
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: 16px;
    line-height: 1.6;
    color: var(--ink);
    background-color: var(--surface);
    resize: vertical;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.form-textarea:hover {
    border-color: var(--purple-300);
}

.form-textarea:focus {
    outline: none;
    border-color: var(--purple-600);
    box-shadow: 0 0 0 3px rgba(128, 128, 255, 0.28);
}

.form-textarea::placeholder,
.form-input::placeholder {
    color: #6f6785;
}

/* script.js toggles these on whichever field failed validation. */
.form-input--invalid,
.form-textarea--invalid {
    border-color: #b0291e;
    background-color: #fffafa;
}

.form-input--invalid:focus,
.form-textarea--invalid:focus {
    border-color: #b0291e;
    box-shadow: 0 0 0 3px rgba(176, 41, 30, 0.18);
}

.form-hint {
    font-size: 13px;
    color: var(--ink-soft);
    font-weight: 400;
}

.required {
    color: #b0291e;
}

.field-error {
    font-size: 13px;
    color: #a32418;
    font-weight: 600;
    min-height: 0;
}

.field-error:empty {
    display: none;
}

.contact-layout {
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: clamp(24px, 4vw, 44px);
    align-items: start;
}

/* The call-or-text column, no box: the page's own rules divide the sections. */
.contact-panel {
    position: sticky;
    top: 24px;
}

.contact-panel h2 {
    font-size: 24px;
    margin-bottom: 10px;
}

.contact-panel p {
    color: var(--ink-soft);
    margin-bottom: 16px;
}

.contact-phone-number {
    display: block;
    font-family: var(--font-display);
    font-size: clamp(24px, 3vw, 32px);
    font-weight: 700;
    color: var(--purple-900);
    text-decoration: none;
    margin: 4px 0 18px;
    word-break: break-word;
}

.contact-phone-number:hover {
    color: var(--purple-700);
    text-decoration: underline;
}

.contact-phone-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 16px;
}

.contact-form {
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.contact-form .form-hint {
    text-align: center;
    margin: 0;
}

.contact-error {
    background: #fdecea;
    border: 1px solid #f3b8b2;
    color: #8d2f1c;
    border-radius: var(--radius-sm);
    padding: 12px 16px;
    font-size: 14px;
    font-weight: 600;
}

.contact-success {
    background-color: var(--surface-sunk);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: clamp(24px, 3.5vw, 36px);
    text-align: center;
}

.contact-success h2 {
    font-size: 26px;
    margin-bottom: 10px;
}

.contact-success p {
    color: var(--ink-soft);
    max-width: 46ch;
    margin: 0 auto 18px;
}

.contact-success-icon {
    width: 56px;
    height: 56px;
    margin: 0 auto 16px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #e6f5ec;
    color: #1c7a45;
}

.contact-success .contact-phone-actions {
    justify-content: center;
    margin-bottom: 4px;
}

/* Honeypot - hidden from humans, filled by bots */
.mailcheck {
    position: absolute;
    left: -9999px;
    opacity: 0;
    height: 0;
    overflow: hidden;
}

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

.site-footer {
    width: 100%;
}

.footer-bottom {
    background-color: var(--purple-900);
    padding: 22px 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

/* The copyright and the ABN share one line in landscape, the ABN pushed to the
   right edge, both sitting on a single baseline. It is a flex row here rather
   than a text-align on one paragraph so the two can keep their different font
   sizes without the baseline drifting. The 820px block below turns it back into
   a centred stack, which is the portrait case. */
.footer-copyright {
    color: var(--purple-200);
    font-size: 14px;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: baseline;
    gap: 4px 24px;
}

/* ------------------------------------------------------------ Spacers --- */

.spacer {
    height: 28px;
}

.spacer-lg {
    height: 48px;
}

/* --------------------------------------------------------- Responsive --- */

@media screen and (max-width: 1024px) {
    .desktop-nav {
        display: none;
    }

    .mobile-menu-toggle {
        display: block;
    }

    .header-menu-sidebar {
        display: block;
    }
}

@media screen and (max-width: 820px) {
    body {
        font-size: 16px;
    }

    .site-title {
        font-size: 17px;
    }

    /* The exception to the 50/50 split: stacked, photo on top. order does it
       because in a single-column grid the DOM order is photo-then-text, which
       is already photo first - order: -1 is what keeps it that way if the
       markup is ever rearranged. */
    .banner-card {
        grid-template-columns: 1fr;
        /* The 20px band along the bottom goes in portrait. Stacked, it is not
           holding the photo and the text together - there is nothing beside it to
           divide from - and 20px of solid purple under a short block of copy is a
           lot of banner for a phone. The banner's edge on the page is the
           shadow, which stays. */
        border-bottom: 0;
    }

    .banner-photo {
        order: -1;
        /* The wrapper is out of flow's counterpart: with the img absolute, the
           column has no intrinsic height of its own, so a phone would get a
           zero-height photo unless it is given one. Banded, not a square. */
        height: clamp(190px, 46vw, 300px);
        /* Both of the wrapper's edges go in portrait. Stacked, the photo is a band
           across the top of the card rather than a panel beside the text, so the
           radius would only show purple in the two top corners, and the right-hand
           margin would cut a 20px gap out of a photo that already runs to the edge
           of the screen. */
        border-radius: 0;
        margin-right: 0;
    }

    .about-image {
        float: none;
        max-width: 100%;
        display: block;
        margin: 0 auto 20px;
    }

    .card img.float-left,
    .card img.float-right {
        float: none;
        display: block;
        margin: 0 auto 12px;
    }

    .contact-panel {
        position: static;
    }

    .footer-bottom {
        flex-direction: column;
        gap: 14px;
        text-align: center;
    }

    /* Portrait stacks the copyright and the ABN again, centred, with the gap
       coming from the row gap so the two sit a line apart rather than a
       paragraph apart. */
    .footer-copyright {
        flex-direction: column;
        align-items: center;
        gap: 4px;
    }
}

@media screen and (max-width: 620px) {
    .contact-layout {
        grid-template-columns: 1fr;
    }

}

/* Portrait phones. Two separate wrappers, .site-content and .container, each
   add 20px of horizontal padding, so the content column was inset 40px a side
   and the background showed either side of it - on a 390px iPhone that left
   262px of text. Narrowing both reclaims 28px a side.

   The banner is deliberately not in this list: it is full bleed, so the two
   wrappers below it narrowing to 6px is what leaves it wider than the content.
   6px rather than 0, so the column's own edge still has a little background
   against it instead of being flush with the screen edge. */
@media screen and (max-width: 480px) {
    .site-content {
        padding-left: 6px;
        padding-right: 6px;
    }

    .container {
        padding-left: 6px;
        padding-right: 6px;
    }

    /* The reason mark narrows with the section. At 72px plus a 28px gap it would
       be taking a third of a 390px screen away from the copy. */
    .reason {
        grid-template-columns: 48px 1fr;
        column-gap: 14px;
    }

    .reason-icon {
        width: 48px;
        height: 40px;
    }
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    .btn,
    .header-menu-sidebar {
        transition: none;
    }
}
