/* roberthowardteaching.co.uk - styles
   Tokens mirror design-system/teaching/MASTER.md. Warm cream ground, deep-green
   action colour, honey accent. Every text pair verified >= 4.5:1 (WCAG AA).
   Mobile-first; motion lives under html.js-anim so no-JS/reduced-motion users
   always see everything. */

/* ---- Fonts (self-hosted, latin subset) ---- */
@font-face {
  font-family: 'Nunito Sans';
  src: url('/fonts/nunito-sans-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Nunito Sans';
  src: url('/fonts/nunito-sans-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Nunito';
  src: url('/fonts/nunito-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Nunito';
  src: url('/fonts/nunito-800.woff2') format('woff2');
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

/* ---- Design tokens ---- */
:root {
  --color-bg: #eff3f7;
  --color-surface: #ffffff;
  --color-surface-tint: #e3eaf1;
  --color-fg: #1e2a36;
  --color-fg-muted: #51606c;
  --color-heading: #16222e;
  --color-primary: #2e4c6d;
  --color-primary-strong: #223a56;
  --color-on-primary: #ffffff;
  --color-accent: #c98a34;
  --color-accent-strong: #a9701f;
  --color-on-accent: #2a1e06;
  --color-border: #d3dce4;
  --color-focus: #2e4c6d;

  --font-body: 'Nunito Sans', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  --font-head: 'Nunito', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;

  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 3rem;
  --space-3xl: 4rem;

  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(23, 35, 46, 0.06);
  --shadow-md: 0 6px 16px rgba(23, 35, 46, 0.1);
  --shadow-lg: 0 14px 30px rgba(23, 35, 46, 0.12);
  --shadow-xl: 0 24px 50px rgba(23, 35, 46, 0.16);

  --container: 68rem;
  --measure: 42rem;
}

/* ---- Reset / base ---- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 5rem;
}
body {
  margin: 0;
  /* Sticky footer: on short pages (privacy, 404) the footer would otherwise
     stop mid-screen and leave a band of page background below it. The skip
     link is absolute and the sticky CTA is fixed, so neither joins this
     column. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1,
h2,
h3 {
  font-family: var(--font-head);
  color: var(--color-heading);
  line-height: 1.15;
  margin: 0 0 var(--space-md);
  font-weight: 800;
  letter-spacing: -0.01em;
}
h1 {
  font-size: clamp(2.1rem, 1.55rem + 2.2vw, 3.25rem);
}
h2 {
  font-size: clamp(1.6rem, 1.2rem + 1.7vw, 2.25rem);
}
h3 {
  font-size: 1.25rem;
  font-weight: 700;
}
p {
  margin: 0 0 var(--space-md);
}
a {
  color: var(--color-primary);
  text-decoration-thickness: 0.08em;
  text-underline-offset: 0.15em;
}
a:hover {
  color: var(--color-primary-strong);
}
img,
video {
  max-width: 100%;
  height: auto;
  display: block;
}
ul {
  margin: 0;
  padding: 0;
}
:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---- Helpers ---- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-lg);
}
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
main {
  flex: 1 0 auto;
}

.skip-link {
  position: absolute;
  left: var(--space-sm);
  top: -4rem;
  z-index: 100;
  background: var(--color-primary);
  color: var(--color-on-primary);
  padding: 0.6rem 1rem;
  border-radius: var(--radius-md);
  transition: top 150ms ease;
}
.skip-link:focus {
  top: var(--space-sm);
  color: var(--color-on-primary);
}
.eyebrow {
  display: inline-block;
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-primary);
  margin-bottom: var(--space-sm);
}
/* Signature: a honey "marker" underline behind key words */
/* ---- Buttons ---- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  min-height: 48px;
  padding: 0.75rem 1.5rem;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.1;
  text-decoration: none;
  border: 2px solid transparent;
  cursor: pointer;
  transition:
    background-color 180ms ease,
    color 180ms ease,
    box-shadow 180ms ease,
    translate 180ms ease;
}
.btn .icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
}
.btn--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
  box-shadow: var(--shadow-sm);
}
.btn--primary:hover {
  background: var(--color-primary-strong);
  color: var(--color-on-primary);
  box-shadow: var(--shadow-md);
}
.btn--accent {
  background: var(--color-accent);
  color: var(--color-on-accent);
  box-shadow: var(--shadow-sm);
}
.btn--accent:hover {
  background: var(--color-accent-strong);
  color: var(--color-on-accent);
  box-shadow: var(--shadow-md);
}
.btn--outline {
  background: transparent;
  color: var(--color-primary);
  border-color: var(--color-primary);
}
.btn--outline:hover {
  background: var(--color-primary);
  color: var(--color-on-primary);
}
.btn--lg {
  min-height: 56px;
  font-size: 1.0625rem;
  padding: 0.9rem 1.9rem;
}
.btn--block {
  width: 100%;
}
.btn--sm {
  min-height: 40px;
  padding: 0.4rem 0.85rem;
  font-size: 0.9rem;
}

/* ---- Header ---- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(239, 243, 247, 0.9);
  backdrop-filter: saturate(1.2) blur(8px);
  border-bottom: 1px solid var(--color-border);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  min-height: 4rem;
}
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.15rem;
  color: var(--color-heading);
  text-decoration: none;
  letter-spacing: -0.01em;
}
.wordmark svg {
  width: 1.9rem;
  height: 1.9rem;
  flex: none;
}
.site-nav {
  display: none;
}
.site-nav ul {
  display: flex;
  gap: var(--space-lg);
  list-style: none;
}
.nav-link {
  color: var(--color-fg);
  text-decoration: none;
  font-weight: 600;
  padding: 0.4rem 0;
  border-bottom: 2px solid transparent;
}
.nav-link:hover {
  color: var(--color-primary);
  border-bottom-color: var(--color-accent);
}
.header-cta {
  display: inline-flex;
}
.header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
/* The header is sticky, so on a phone these are the only always-visible way to
   call or message - they get a full 44px touch target. Back to 40px at 48em,
   where the layout is unchanged. */
.header-actions .btn {
  min-height: 44px;
}
/* Header buttons are icon-only on small screens, icon + label once there is room. */
.header-actions .btn__label {
  display: none;
}
@media (min-width: 30em) {
  .header-actions .btn__label {
    display: inline;
  }
}

/* ---- Sections ---- */
.section {
  padding-block: var(--space-3xl);
}
.section--tint {
  background: var(--color-surface-tint);
}
.section__head {
  max-width: var(--measure);
  margin-bottom: var(--space-2xl);
}
.section__lede {
  color: var(--color-fg-muted);
  font-size: 1.1rem;
}
.section--center .section__head {
  margin-inline: auto;
  text-align: center;
}

/* ---- Hero ---- */
.hero {
  padding-block: var(--space-2xl) var(--space-3xl);
}
.hero__inner {
  display: grid;
  gap: var(--space-xl);
  align-items: center;
}
.hero__title {
  margin-bottom: var(--space-md);
}
.hero__lede {
  font-size: 1.2rem;
  color: var(--color-fg-muted);
  max-width: 34rem;
}
.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  margin-top: var(--space-lg);
}
.hero__note {
  margin-top: var(--space-md);
  font-size: 0.95rem;
  color: var(--color-fg-muted);
}
.hero__media {
  position: relative;
}
.hero__media img {
  width: 100%;
  max-width: 24rem;
  margin-inline: auto;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--color-border);
}

/* ---- Trust strip ---- */
.trust-strip {
  background: var(--color-primary);
  color: var(--color-on-primary);
}
.trust-strip__list {
  list-style: none;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  padding-block: var(--space-xl);
}
.trust-item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-weight: 600;
  /* Slightly tighter than body copy so the longest badge stays on one line. */
  font-size: 0.95rem;
}
.trust-item .icon {
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  color: var(--color-accent);
}

/* ---- Cards (generic) ---- */
.card-grid {
  display: grid;
  gap: var(--space-lg);
  list-style: none;
  padding: 0;
}
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--shadow-md);
}
.card h3 {
  margin-bottom: var(--space-sm);
}
.card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-md);
  background: var(--color-surface-tint);
  color: var(--color-primary);
  margin-bottom: var(--space-md);
}
.card__icon .icon {
  width: 1.5rem;
  height: 1.5rem;
}
.card--primary {
  border-color: var(--color-primary);
  border-width: 2px;
}
.card--primary .card__icon {
  background: var(--color-primary);
  color: var(--color-on-primary);
}
.card p:last-child {
  margin-bottom: 0;
}
.card__meta {
  color: var(--color-fg-muted);
  font-size: 0.95rem;
}

/* ---- About ---- */
/* Single column: readable text width, then a full-width room gallery below. */
.about__grid {
  display: grid;
  gap: var(--space-xl);
}
.about__text {
  max-width: var(--measure);
}
.about__text p:last-child {
  margin-bottom: 0;
}
.gallery {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-md);
}
.gallery img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm);
}
.gallery figure {
  margin: 0;
}
/* Two-up, an odd last photo would sit alone against the left edge. Let it span
   the row but hold it to one column's width so it centres under the pair.
   50% of the grid minus half the gap is exactly one column. */
.gallery figure:last-child:nth-child(odd) {
  grid-column: 1 / -1;
  width: calc(50% - var(--space-md) / 2);
  margin-inline: auto;
}
.gallery figcaption {
  font-size: 0.85rem;
  color: var(--color-fg-muted);
  margin-top: var(--space-xs);
}

/* ---- Teaches ---- */
.teach-note {
  margin-top: var(--space-xl);
  color: var(--color-fg-muted);
  font-weight: 600;
}

/* ---- Testimonials ---- */
.testimonial-grid {
  display: grid;
  gap: var(--space-lg);
}
.testimonial {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  box-shadow: var(--shadow-md);
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.testimonial__quote {
  font-size: 1.1rem;
  margin: 0;
  position: relative;
}
.testimonial__quote::before {
  content: '\201C';
  font-family: var(--font-head);
  font-size: 3rem;
  line-height: 0;
  color: var(--color-accent);
  vertical-align: -0.35em;
  margin-right: 0.15em;
}
/* Real reviews run from 10 words to 130. The quote is clipped to a fixed
   number of lines so every card is the same size, and anything longer gets a
   button to open it in place. Both only exist once JS is running - without it
   the quote is simply shown in full, because a review is content, not
   decoration. */
/* Every quote occupies the full eight lines, padded if it is shorter, so the
   cards match each other on a phone too - there the grid is one column, so each
   card is its own row and nothing equalises them. 1.65em is the inherited line
   height against the quote's own 1.1rem. */
html.js-clamp .testimonial__quote {
  min-height: calc(8 * 1.65em);
}
html.js-clamp .testimonial--clipped .testimonial__quote {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 8;
  line-clamp: 8;
  overflow: hidden;
}
html.js-clamp .testimonial--clipped {
  cursor: pointer;
}
/* Full review in an overlay, so opening one leaves the grid untouched.
   <dialog> brings its own focus trap, Esc handling and backdrop. */
.review-dialog {
  width: min(38rem, calc(100vw - 2 * var(--space-lg)));
  max-height: min(80vh, 44rem);
  padding: var(--space-xl);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-fg);
  box-shadow: var(--shadow-xl);
  overflow: auto;
}
.review-dialog::backdrop {
  background: rgba(22, 34, 46, 0.55);
}
.review-dialog__quote {
  font-size: 1.15rem;
  margin: 0 0 var(--space-lg);
  white-space: pre-line;
}
.review-dialog__name {
  font-weight: 700;
  color: var(--color-heading);
  margin: 0;
}
.review-dialog__close {
  display: block;
  margin: var(--space-lg) 0 0 auto;
}
.testimonial__more {
  align-self: flex-start;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--color-primary);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
.testimonial__more:hover {
  color: var(--color-primary-strong);
}
.testimonial figcaption {
  margin-top: auto;
  font-style: normal;
  /* The read-more control sits on the name's row rather than adding a row of
     its own, which only the clipped cards would have had. */
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
}
.testimonial cite {
  font-style: normal;
  font-weight: 700;
  color: var(--color-heading);
}
.testimonial__context {
  display: block;
  color: var(--color-fg-muted);
  font-size: 0.92rem;
}
.stars {
  display: inline-flex;
  gap: 2px;
  color: var(--color-accent-strong);
}
.stars .icon {
  width: 1.1rem;
  height: 1.1rem;
}
.stars .star--off {
  color: var(--color-border);
}

/* Hide the dynamic sections when the data files publish nothing. */
.testimonials:not(:has(.testimonial)) {
  display: none;
}

/* ---- Contact ---- */
.contact {
  text-align: center;
}
.contact .section__head {
  margin-inline: auto;
}
.contact__methods {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-md);
  margin-block: var(--space-xl);
}
.contact__methods .btn {
  flex: 1 1 14rem;
  max-width: 20rem;
  /* Let the row wrap a whole button onto the next line rather than breaking a
     label across two lines inside the pill. */
  white-space: nowrap;
}
.contact__detail {
  color: var(--color-fg-muted);
  font-size: 0.98rem;
}
.contact__detail a {
  font-weight: 700;
}
.contact__note {
  max-width: var(--measure);
  margin: var(--space-lg) auto 0;
  color: var(--color-fg-muted);
  font-size: 0.95rem;
}

/* ---- Footer ---- */
.site-footer {
  background: var(--color-heading);
  color: #e7efe9;
  padding-block: var(--space-2xl);
}
.site-footer a {
  color: #fff;
}
.site-footer__grid {
  display: grid;
  gap: var(--space-lg);
  align-items: start;
}
.site-footer nav ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-lg);
}
.site-footer__small {
  color: #b9cabf;
  font-size: 0.9rem;
}

/* ---- Sticky mobile CTA ---- */
/* ---- Utilities (avoid inline styles so the CSP needs no 'unsafe-inline') ---- */
.u-center {
  text-align: center;
}
.u-narrow {
  max-width: 46rem;
  margin-inline: auto;
}
.u-mt-sm {
  margin-top: 0.75rem;
}
.u-mt-lg {
  margin-top: var(--space-lg);
}
.u-mt-xl {
  margin-top: var(--space-xl);
}
/* ---- Motion (only when JS + motion allowed) ---- */
.reveal {
  opacity: 1;
}
html.js-anim .reveal {
  opacity: 0;
  translate: 0 14px;
  transition:
    opacity 500ms ease,
    translate 500ms ease;
}
html.js-anim .reveal.is-visible {
  opacity: 1;
  translate: 0 0;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  html.js-anim .reveal {
    opacity: 1;
    translate: none;
  }
}

/* ---- Very large screens (4K TVs) ----
   A 4K TV browser that reports its full 3840 CSS pixels left the page as a
   1040px ribbon - 27% of the screen - with 17px body text, which is unreadable
   from across a room. Everything here is sized in rem, including the 68rem
   container, so lifting the root size scales type, spacing and layout width
   together rather than just stretching the columns to an unreadable line
   length. Starts above every normal desktop monitor: 2560 is a 1440p display
   and is deliberately left alone, as is 1920. */
@media (min-width: 2600px) {
  html {
    font-size: 20px;
  }
}
@media (min-width: 3400px) {
  html {
    font-size: 26px;
  }
}

/* ---- Responsive ---- */
@media (min-width: 40em) {
  .trust-strip__list {
    grid-template-columns: repeat(2, 1fr);
  }
  .card-grid--3 {
    grid-template-columns: repeat(2, 1fr);
  }
  .testimonial-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  /* Same idea as the gallery: two-up, an odd last card sits alone against the
     left edge, so let it span the row at exactly one column's width and centre
     it under the pair. Both grids use --space-lg as their gap. */
  .card-grid--3 > :last-child:nth-child(odd),
  .testimonial-grid > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
    width: calc(50% - var(--space-lg) / 2);
    margin-inline: auto;
  }
  .gallery {
    grid-template-columns: repeat(3, 1fr);
  }
  /* Three-up they all fit on one row, so the lone-photo rule is off again. */
  .gallery figure:last-child:nth-child(odd) {
    grid-column: auto;
    width: auto;
  }
}

@media (min-width: 48em) {
  .site-nav {
    display: block;
  }
  .header-actions .btn {
    min-height: 40px;
  }
}

/* The hero splits as soon as the text column is wide enough to carry the
   headline - around 470px here. Any earlier and the headline ran to six lines
   on an iPad; any later and near-desktop widths sat stacked with the photo
   marooned in whitespace. 54em rather than 56em so a Galaxy Tab S9+ held
   upright (876px) lands inside it rather than 20px short. The trust strip
   needs more room than this, so it goes three-up separately at 64em. */
/* Stacked, but with width to spare: centre the block. Left-aligned it read as
   left-heavy with the right-hand side empty, which is what a tablet held
   upright looks like at 800px. */
@media (min-width: 48em) {
  .hero__content {
    text-align: center;
  }
  .hero__lede,
  .hero__note {
    margin-inline: auto;
  }
  .hero__cta {
    justify-content: center;
  }
}

/* Cards wait until 54em: three-up any earlier and each card would be narrower
   than it is on a phone. */
@media (min-width: 54em) {
  .card-grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }
  .testimonial-grid {
    grid-template-columns: repeat(3, 1fr);
  }
  /* Three-up the two-column rule no longer applies. */
  .card-grid--3 > :last-child:nth-child(odd),
  .testimonial-grid > :last-child:nth-child(odd) {
    grid-column: auto;
    width: auto;
  }
  /* ...but a count of 3n+1 still strands the last one on a row of its own, so
     centre it the same way. Must come after the reset above: equal specificity,
     so the later rule wins. Ten reviews hit this. */
  .card-grid--3 > :last-child:nth-child(3n + 1),
  .testimonial-grid > :last-child:nth-child(3n + 1) {
    grid-column: 1 / -1;
    width: calc((100% - 2 * var(--space-lg)) / 3);
    margin-inline: auto;
  }
}

/* Side by side only from here. Below this the text block is tall enough that
   filling the column would crop the photo to about half its width, cutting the
   piano and his hands out of frame - the whole point of the shot. At 1280 the
   text and the uncropped photo come out the same height on their own. */
@media (min-width: 64em) {
  .hero__inner {
    grid-template-columns: 1.05fr 0.95fr;
    align-items: center;
  }
  .hero__media img {
    max-width: none;
  }
  .hero__content {
    text-align: left;
  }
  .hero__lede,
  .hero__note {
    margin-inline: 0;
  }
  .hero__cta {
    justify-content: flex-start;
  }
  .trust-strip__list {
    grid-template-columns: repeat(3, 1fr);
  }
}
