
/*
 * Tailwind's preflight used to supply the reset. Almost everything it did for this
 * page is already stated explicitly further down (margins, list styles, `font:
 * inherit` on controls), so what remains is the handful of browser defaults this
 * design actually depends on.
 */
:root {
  --font-display: "Bricolage Grotesque";
  --font-body: "Public Sans";
  --font-mono: "DM Mono";
}

h4, h5, h6, figure, pre, blockquote, dl, dd { margin: 0; }
ul, ol, menu { list-style: none; margin: 0; padding: 0; }
button, input, select, textarea { font-family: inherit; font-size: 100%; margin: 0; }
button { background: none; border: 0; padding: 0; color: inherit; }
img, svg, video { display: block; max-width: 100%; }
table { border-collapse: collapse; }
::placeholder { color: var(--ink-faint); opacity: 1; }
/*
  Palette taken from a natural dyer's shelf — indigo vat, weld yellow, cochineal
  pink — on undyed cool wool rather than the warm cream this kind of site usually
  reaches for. Indigo carries the yarn in the seat gauge, ink carries the actions, and
  cochineal is reserved for "full" and for cancelling.
*/
:root {
  --ink: #16192b;
  --ink-soft: #4b5068;
  --ink-faint: #767b91;
  --paper: #e9eae3;
  --paper-raised: #f7f7f3;
  --indigo: #2f3d91;
  --indigo-soft: #6f7ac0;
  --saffron: #e0a52c;
  --cochineal: #ab3560;
  /* Weld overdyed in the indigo vat: the dyer's green, muted enough to sit beside
     indigo without shouting, dark enough to read on paper. */
  --weld-green: #3c6b4a;
  --line: #cbcdc2;
  --line-strong: #a9ac9e;

  --radius: 5px;
  --measure: 68ch;
  --page: 1120px;

  /* A single knitted stitch, used as a repeating band and as a bullet. */
  --stitch-band: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='12' viewBox='0 0 14 12'%3E%3Cpath d='M2 1.5 L7 9 L12 1.5' fill='none' stroke='%232f3d91' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

* {
  box-sizing: border-box;
}

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body), system-ui, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.6;
  font-synthesis-weight: none;
}

h1, h2, h3 {
  font-family: var(--font-display), system-ui, sans-serif;
  font-weight: 700;
  letter-spacing: -0.021em;
  line-height: 1.1;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0; }

a {
  color: inherit;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 3px;
}
a:hover { text-decoration-color: var(--saffron); }

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

/* ---------- Layout ---------- */

.page {
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: clamp(1.15rem, 4vw, 2.5rem);
}

.stack > * + * { margin-top: var(--gap, 1rem); }

/* ---------- Site chrome ---------- */

/*
  The header sits under a band of knitted stitches. The seat gauge counts in skeins
  of yarn; this is the fabric they become, so the page opens on both ends of the craft.
*/
.siteHeader {
  border-top: 4px solid var(--ink);
  background-image: var(--stitch-band);
  background-repeat: repeat-x;
  background-position: top 8px left 0;
  background-size: 14px 12px;
  padding-top: 30px;
  margin-bottom: 2.5rem;
}

.siteHeader__inner {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-block: 1rem 1.15rem;
  border-bottom: 1px solid var(--line);
}

/* A flex row, because the logo and the name may both be in here, side by side. */
.siteHeader__name {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-display), system-ui, sans-serif;
  font-weight: 800;
  font-size: 1.3rem;
  letter-spacing: -0.02em;
  text-decoration: none;
}

/*
  An uploaded logo stands in the header, with or without the name beside it.
  Height-bounded rather than width-bounded, because the one thing every logo has in common
  here is that it sits on a single line of type: a tall one would push the header open, a
  wide one only runs out towards the language switch, which the flex row already handles.
*/
.siteHeader__logo {
  max-height: 2.5rem;
  width: auto;
}

.siteFooter {
  margin-top: 4.5rem;
  padding-block: 1.5rem 3rem;
  border-top: 1px solid var(--line);
  color: var(--ink-faint);
  font-size: 0.875rem;
}

.siteFooter__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  align-items: baseline;
  justify-content: space-between;
}

/*
  The colophon sits under the row above and takes the same flex shape, so the two agree
  about where to break on a narrow screen. A step smaller, because it is the sort of thing
  someone reads once when they go looking for it.
*/
.siteFooter__colophon {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.5rem;
  align-items: baseline;
  justify-content: space-between;
  margin-top: 0.75rem;
  font-size: 0.82rem;
  color: var(--ink-faint);
}

/* ---------- Type helpers ---------- */

.label {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-faint);
}

.mono {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.9rem;
}

.display {
  font-size: clamp(2.1rem, 5.2vw, 3.25rem);
  font-weight: 800;
}

.lede {
  color: var(--ink-soft);
  max-width: var(--measure);
}

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

/* ---------- Yarn gauge (the seat counter) ---------- */

/*
  Capacity drawn as skeins of yarn: one per place, inked in when someone holds it. It
  maps one-to-one onto the numbered seats the database hands out, so what you see is
  literally the seating.

  The artwork is fine line work and turns to a smudge below about 40px tall, which is
  what sets the size here — smaller would be tidier and unreadable.
*/
.gauge {
  display: flex;
  flex-wrap: wrap;
  gap: 5px 4px;
}

.gauge__skein {
  display: block;
  width: 28px;
  height: 41.5px;
  flex: none;
  /* The paths are currentColor, so state is only ever a colour. */
  color: var(--line-strong);
  opacity: 0.55;
}

.gauge__skein--taken {
  color: var(--indigo);
  opacity: 1;
}

.gauge__skein--full {
  color: var(--cochineal);
  opacity: 1;
}

/*
  Two-thirds size, for the gauge that sits inside a form header rather than carrying a
  page. Still above the ~28px where the line work turns to a smudge.
*/
.gauge--small .gauge__skein {
  width: 19px;
  height: 28px;
}

/* The <symbol> holder must take up no space. */
.spriteSheet {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* One orchestrated moment: the gauge fills itself in on the course page. */
.gauge--animate .gauge__skein {
  animation: skeinIn 320ms cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
}

@keyframes skeinIn {
  from { opacity: 0; transform: translateY(-4px); }
}

.gauge--animate .gauge__skein:nth-child(1) { animation-delay: 0ms; }
.gauge--animate .gauge__skein:nth-child(2) { animation-delay: 28ms; }
.gauge--animate .gauge__skein:nth-child(3) { animation-delay: 56ms; }
.gauge--animate .gauge__skein:nth-child(4) { animation-delay: 84ms; }
.gauge--animate .gauge__skein:nth-child(5) { animation-delay: 112ms; }
.gauge--animate .gauge__skein:nth-child(6) { animation-delay: 140ms; }
.gauge--animate .gauge__skein:nth-child(7) { animation-delay: 168ms; }
.gauge--animate .gauge__skein:nth-child(8) { animation-delay: 196ms; }
.gauge--animate .gauge__skein:nth-child(9) { animation-delay: 224ms; }
.gauge--animate .gauge__skein:nth-child(10) { animation-delay: 252ms; }
.gauge--animate .gauge__skein:nth-child(11) { animation-delay: 280ms; }
.gauge--animate .gauge__skein:nth-child(12) { animation-delay: 308ms; }
.gauge--animate .gauge__skein:nth-child(13) { animation-delay: 336ms; }
.gauge--animate .gauge__skein:nth-child(14) { animation-delay: 364ms; }
.gauge--animate .gauge__skein:nth-child(15) { animation-delay: 392ms; }
.gauge--animate .gauge__skein:nth-child(16) { animation-delay: 420ms; }
.gauge--animate .gauge__skein:nth-child(17) { animation-delay: 448ms; }
.gauge--animate .gauge__skein:nth-child(18) { animation-delay: 476ms; }
.gauge--animate .gauge__skein:nth-child(19) { animation-delay: 504ms; }
.gauge--animate .gauge__skein:nth-child(20) { animation-delay: 532ms; }
.gauge--animate .gauge__skein:nth-child(21) { animation-delay: 560ms; }
.gauge--animate .gauge__skein:nth-child(22) { animation-delay: 588ms; }
.gauge--animate .gauge__skein:nth-child(23) { animation-delay: 616ms; }
.gauge--animate .gauge__skein:nth-child(24) { animation-delay: 644ms; }
.gauge--animate .gauge__skein:nth-child(25) { animation-delay: 672ms; }
.gauge--animate .gauge__skein:nth-child(26) { animation-delay: 700ms; }
.gauge--animate .gauge__skein:nth-child(27) { animation-delay: 728ms; }
.gauge--animate .gauge__skein:nth-child(28) { animation-delay: 756ms; }
.gauge--animate .gauge__skein:nth-child(29) { animation-delay: 784ms; }
.gauge--animate .gauge__skein:nth-child(30) { animation-delay: 812ms; }
.gauge--animate .gauge__skein:nth-child(31) { animation-delay: 840ms; }
.gauge--animate .gauge__skein:nth-child(32) { animation-delay: 868ms; }
.gauge--animate .gauge__skein:nth-child(33) { animation-delay: 896ms; }
.gauge--animate .gauge__skein:nth-child(34) { animation-delay: 924ms; }
.gauge--animate .gauge__skein:nth-child(35) { animation-delay: 952ms; }
.gauge--animate .gauge__skein:nth-child(36) { animation-delay: 980ms; }
.gauge--animate .gauge__skein:nth-child(37) { animation-delay: 1008ms; }
.gauge--animate .gauge__skein:nth-child(38) { animation-delay: 1036ms; }
.gauge--animate .gauge__skein:nth-child(39) { animation-delay: 1064ms; }
.gauge--animate .gauge__skein:nth-child(40) { animation-delay: 1092ms; }
.gauge--animate .gauge__skein:nth-child(41) { animation-delay: 1120ms; }
.gauge--animate .gauge__skein:nth-child(42) { animation-delay: 1148ms; }
.gauge--animate .gauge__skein:nth-child(43) { animation-delay: 1176ms; }
.gauge--animate .gauge__skein:nth-child(44) { animation-delay: 1204ms; }
.gauge--animate .gauge__skein:nth-child(45) { animation-delay: 1232ms; }
.gauge--animate .gauge__skein:nth-child(46) { animation-delay: 1260ms; }
.gauge--animate .gauge__skein:nth-child(47) { animation-delay: 1288ms; }
.gauge--animate .gauge__skein:nth-child(48) { animation-delay: 1316ms; }
.gauge--animate .gauge__skein:nth-child(49) { animation-delay: 1344ms; }
.gauge--animate .gauge__skein:nth-child(50) { animation-delay: 1372ms; }
.gauge--animate .gauge__skein:nth-child(51) { animation-delay: 1400ms; }
.gauge--animate .gauge__skein:nth-child(52) { animation-delay: 1428ms; }
.gauge--animate .gauge__skein:nth-child(53) { animation-delay: 1456ms; }
.gauge--animate .gauge__skein:nth-child(54) { animation-delay: 1484ms; }
.gauge--animate .gauge__skein:nth-child(55) { animation-delay: 1512ms; }
.gauge--animate .gauge__skein:nth-child(56) { animation-delay: 1540ms; }
.gauge--animate .gauge__skein:nth-child(57) { animation-delay: 1568ms; }
.gauge--animate .gauge__skein:nth-child(58) { animation-delay: 1596ms; }
.gauge--animate .gauge__skein:nth-child(59) { animation-delay: 1624ms; }
.gauge--animate .gauge__skein:nth-child(60) { animation-delay: 1652ms; }

@media (prefers-reduced-motion: reduce) {
  .gauge--animate .gauge__skein { animation: none; }
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

.seatCount {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.9rem;
  color: var(--ink-soft);
}
.seatCount strong { color: var(--ink); font-weight: 500; }
.seatCount--full { color: var(--cochineal); }

/* ---------- Price ---------- */

/*
  Saffron was the one colour on the shelf doing nothing but tinting a link on hover, and
  it is the right weight for money: warm enough to find on the page, quiet enough not to
  argue with indigo, which is the customer's to rebrand. The price never takes the accent,
  so a deployment cannot end up with a price the same colour as its own buttons.
*/

/* The card's line, sitting beside the seat count in the footer. */
.priceTag {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink);
}

/*
  The booking panel's block. Ruled off above with a saffron hairline, echoing the stitched
  band under the header, so the price reads as its own statement rather than as a third
  line of seat text.
*/
.price {
  padding-top: 0.9rem;
  border-top: 2px solid var(--saffron);
}

.price__amount {
  font-family: var(--font-display), system-ui, sans-serif;
  font-size: clamp(1.5rem, 4vw, 1.85rem);
  font-weight: 700;
  letter-spacing: -0.021em;
  line-height: 1.1;
}

/* Same treatment as .label, so the panel has one voice for its small print. */
.price__unit {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: 0.2rem;
}

.price__note {
  font-size: 0.85rem;
  color: var(--ink-soft);
  margin-top: 0.45rem;
}

/* ---------- Course cards ---------- */

.courseList {
  display: grid;
  gap: 1.15rem;
  padding: 0;
  margin: 0;
  list-style: none;
}

.courseCard {
  position: relative;
  display: grid;
  gap: 0.9rem;
  padding: 1.4rem clamp(1.1rem, 3vw, 1.75rem) 1.5rem;
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.courseCard__title {
  font-size: clamp(1.35rem, 2.6vw, 1.75rem);
}

.courseCard .courseCard__title a { text-decoration: none; }
/* The stretched link only makes sense inside a card, which is the positioned ancestor that
   bounds it. Scoped to .courseCard because the booking summary reuses this heading class:
   unscoped, the overlay found no positioned ancestor there and covered the whole page,
   swallowing clicks meant for the form fields below it. */
.courseCard .courseCard__title a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
}
.courseCard:hover { border-color: var(--line-strong); }
.courseCard:has(a:focus-visible) { border-color: var(--indigo); }

.courseCard__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  color: var(--ink-soft);
  font-size: 0.9rem;
}

/*
  A picture turns the card into two columns — the words on the left, the picture on the
  right — and it stands beside the gauge as well as the title, in the space the card was
  already using. Given a row of its own it would add its own height to every card; here it
  costs none. Keyed off :has rather than a wrapper element, so a card without a picture is
  the same markup and the same rules it always was.

     +-------------------------------+
     | gauge          |  picture     |
     | title, dates   |              |
     +-------------------------------+
     | price                   seats |
     +-------------------------------+
*/
.courseCard:has(.courseCard__thumb) { grid-template-columns: minmax(0, 1fr) auto; }

.courseCard:has(.courseCard__thumb) > .gauge { grid-area: 1 / 1; }
.courseCard:has(.courseCard__thumb) > .courseCard__body { grid-area: 2 / 1; }
.courseCard:has(.courseCard__thumb) > .courseCard__thumb { grid-area: 1 / 2 / span 2; }
.courseCard:has(.courseCard__thumb) > .courseCard__foot { grid-column: 1 / -1; }

/* Square and cropped, because the operator's photo is whatever shape it is and a list
   only reads as a list while every thumbnail is the same size. Top-aligned, so a card
   with two lines of dates does not stretch it and break that. */
.courseCard__thumb {
  width: 6rem;
  height: 6rem;
  object-fit: cover;
  align-self: start;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.courseCard__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
}

/*
  Price and seats read together — both are facts about the same offer — so they stay one
  group and the action label keeps the far end of the row to itself.
*/
.courseCard__facts {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.9rem;
}

/* ---------- Course detail ---------- */

.courseLayout {
  display: grid;
  gap: 2.5rem;
  align-items: start;
}

@media (min-width: 62rem) {
  .courseLayout {
    grid-template-columns: minmax(0, 1fr) 22.5rem;
    gap: 3.5rem;
  }
  .bookingPanel { position: sticky; top: 2rem; }
}

/* Cropped to one shape, so the page's rhythm does not change with the photograph. */
.courseHero {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.prose {
  max-width: var(--measure);
  color: var(--ink-soft);
  white-space: pre-line;
}

/*
  Sessions are numbered because a course really is a sequence — week 1 builds on
  week 2 — not because numbers look tidy.
*/
.sessions {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: session;
  border-top: 1px solid var(--line);
}

/*
  A heading above the list needs more room under it than a heading above text does.
  Headings here are line-height 1.1 and uppercase, so there is no half-leading beneath
  them, and the list opens on a hairline rather than on a line of type — two hard edges
  with only the stack's gap between them. The same gap that looks right above a
  paragraph looks pinched above a rule, so this adds back what the leading would have
  given. Not applied to a list that follows anything else, and beaten later by
  .bookingSummary .sessions, which wants no margin at all.
*/
.label + .sessions { margin-top: 0.9rem; }

.sessions li {
  counter-increment: session;
  display: flex;
  gap: 1rem;
  align-items: baseline;
  padding-block: 0.7rem;
  border-bottom: 1px solid var(--line);
}

.sessions li::before {
  content: counter(session, decimal-leading-zero);
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.75rem;
  color: var(--indigo-soft);
  min-width: 1.6em;
}

.sessions time {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.875rem;
}

/* ---------- Booking panel and forms ---------- */

.bookingPanel {
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.4rem clamp(1.1rem, 3vw, 1.5rem) 1.5rem;
}

.bookingPanel__head {
  padding-bottom: 1.1rem;
  margin-bottom: 1.2rem;
  border-bottom: 1px solid var(--line);
  display: grid;
  gap: 0.75rem;
}

/*
  Moved up out of the Admin section, which is where it was written and where it no longer
  belongs: the public booking form uses it too. A shared form utility filed under Admin is
  exactly the drift the section headings in this file exist to prevent.
*/
.formGrid {
  display: grid;
  gap: 0 1.25rem;
  grid-template-columns: 1fr;
}

/*
  Once a grid has two columns, its fields line up with each other by subgrid rather than
  each being its own little grid stretched to whatever its taller neighbour needs. Without
  it, a field carrying a hint makes the row tall, the field beside it stretches to match,
  and its label and input drift out of line with the ones next to them — which is exactly
  as untidy as it sounds, and gets worse the more hints there are.

  Four rows because that is the most a field ever has: label, control, hint, error. A field
  with fewer leaves the last rows empty and still lines up with the one beside it, which is
  the whole point — the alignment must not depend on which fields happen to have something
  to say today.

  The rows come from the parent now, so a field gives up its own bottom margin and the
  parent's row-gap separates rows of fields instead, for both columns at once.
*/
.formGrid--aligned > .field {
  grid-row: span 4;
  grid-template-rows: subgrid;
  grid-template-columns: 1fr;
  row-gap: 0.3rem;
  margin-bottom: 0;
}

.formGrid--aligned { row-gap: 1rem; }

@media (min-width: 46rem) {
  .formGrid--two { grid-template-columns: 1fr 1fr; }
}

/*
  Postcode and town on one line, with the postcode box sized to what goes in it — a
  postcode field as wide as a town field invites the wrong thing to be typed in it. The
  breakpoint is earlier than --two's because both of these are short.
*/
@media (min-width: 34rem) {
  .formGrid--postcode { grid-template-columns: minmax(7rem, 0.35fr) 1fr; }
}

.field { display: grid; gap: 0.3rem; margin-bottom: 1rem; }

.field label {
  font-weight: 600;
  font-size: 0.9rem;
}

.field input,
.field textarea,
.field select {
  font: inherit;
  font-size: 1rem;
  padding: 0.6rem 0.7rem;
  background: #fff;
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  width: 100%;
}

.field input:focus-visible,
.field textarea:focus-visible,
.field select:focus-visible {
  outline: 2px solid var(--indigo);
  outline-offset: 1px;
  border-color: var(--indigo);
}

/*
  The accent choices, as a row of dyed swatches. Radio inputs doing the work, so the
  whole control is one form field with no script behind it — the input is taken off the
  page and its label becomes the thing you press.

  Each swatch's colour arrives as --swatch, set by a class the settings page emits from
  the same list it renders. Keeping the hexes in one place is the point: a swatch that
  showed a colour other than the one it sets would be a liar.
*/
.swatchGrid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  border: 0;
  padding: 0;
  margin: 0;
}

.swatchGrid__radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.swatchGrid__swatch {
  display: block;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  background: var(--swatch);
  border: 2px solid var(--paper-raised);
  box-shadow: 0 0 0 1px var(--line-strong);
  cursor: pointer;
}

/* The chosen one wears a ring in its own colour, so the selection reads at a glance. */
.swatchGrid__radio:checked + .swatchGrid__swatch {
  box-shadow: 0 0 0 2px var(--swatch);
}

.swatchGrid__radio:focus-visible + .swatchGrid__swatch {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* Names carry the meaning for a screen reader; the colours carry it for everyone else. */
.visuallyHidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.field__hint { font-size: 0.8rem; color: var(--ink-faint); }
.field__error { font-size: 0.85rem; color: var(--cochineal); font-weight: 500; }
.field--invalid input { border-color: var(--cochineal); }

/* ---------- The booking page ---------- */

/*
  The form left the sticky panel for a page of its own once it started asking for a
  billing address and a signature on the terms: 22.5rem is the right width for a seat
  count and a price, and the wrong width for the form that settles what someone owes.
*/
.bookingForm { max-width: 44rem; counter-reset: formSection; }

/*
  What is being booked, above the form that books it. Nothing separated the two, so the
  card's bottom border sat directly on the first legend and the page read as one block
  with a line through it.

  2.5rem because that is what the site already puts between page-level blocks: the
  header's own margin, and the gap in .courseLayout. The form's 2.25rem is the rhythm
  *within* a form, and using it here would make leaving the card feel like moving between
  two of its sections.
*/
.bookingSummary {
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem clamp(1.1rem, 3vw, 1.4rem);
  margin-bottom: 2.5rem;
  max-width: 44rem;
  display: grid;
  gap: 1.1rem;
}

.bookingSummary .sessions { margin: 0; }
.bookingSummary .price { margin-top: 0; }

.formSection { border: 0; padding: 0; margin: 0 0 2.25rem; }

.formSection__legend {
  display: flex;
  align-items: baseline;
  gap: 0.65rem;
  padding: 0;
  margin-bottom: 1rem;
  font-family: var(--font-display), system-ui, sans-serif;
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.021em;
}

/*
  Numbered by a counter, in the same leading-zero mono the session list uses. A form and a
  course are both sequences, and the site should count them the same way. The numbers are
  decorative — the legend text is what carries the meaning — so they stay out of the markup.
*/
.formSection__legend::before {
  counter-increment: formSection;
  content: counter(formSection, decimal-leading-zero);
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--indigo-soft);
}

/*
  The one checkbox on the site. .field is a label-over-input stack, which is the wrong
  shape here: a consent box is a control with a sentence hanging off it, so the box comes
  first and the sentence wraps beside it.
*/
.checkField {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.35rem 0.7rem;
  align-items: start;
  margin-bottom: 1rem;
}

.checkField input[type="checkbox"] {
  width: 1.15rem;
  height: 1.15rem;
  margin: 0.15rem 0 0;
  accent-color: var(--indigo);
}

.checkField label { font-weight: 500; font-size: 0.95rem; }

/* Everything under the sentence lines up with it, not with the box. */
.checkField .field__hint,
.checkField .field__error,
.checkField .disclosure { grid-column: 2; }

/* No border to redden, so the invalid state is the box itself and the message. */
.checkField--invalid input[type="checkbox"] {
  accent-color: var(--cochineal);
  outline: 2px solid var(--cochineal);
  outline-offset: 2px;
}

.checkField--invalid label { color: var(--cochineal); }

/* ---------- Terms ---------- */

/*
  The operator writes plain text and views.ParseProse turns it into headings, paragraphs
  and lists. This is the one place in this file where styling elements rather than classes
  is right: the markup is generated, so there is nothing to hang a class on.
*/
.legalText { max-width: var(--measure); color: var(--ink-soft); }
.legalText > * + * { margin-top: 0.9rem; }

/*
  A clause is found by its heading, so the gap above one has to be unmistakably larger
  than the gap between paragraphs — otherwise the page reads as one undifferentiated wall
  and "the cancellation section" is something you have to hunt for rather than see.
*/
.legalText h2 {
  font-size: 1.2rem;
  color: var(--ink);
  margin-top: 2.6rem;
}

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

.legalText ul { padding-left: 1.2rem; }
/* Re-declared: the reset at the top of this file strips the marker from every ul. */
.legalText li { list-style: disc; margin-top: 0.35rem; }

/* Bots fill this in; people never see it. */
.honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font: inherit;
  font-family: var(--font-display), system-ui, sans-serif;
  font-weight: 600;
  font-size: 0.98rem;
  padding: 0.62rem 1.15rem;
  border-radius: 4px;
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  cursor: pointer;
  text-decoration: none;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}

.btn:hover { background: var(--indigo); border-color: var(--indigo); }
.btn:disabled { opacity: 0.55; cursor: progress; }
.btn--block { width: 100%; }

.btn--quiet {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}
.btn--quiet:hover { background: transparent; border-color: var(--ink); color: var(--ink); }

.btn--danger {
  background: transparent;
  color: var(--cochineal);
  border-color: var(--cochineal);
}
.btn--danger:hover { background: var(--cochineal); color: var(--paper-raised); border-color: var(--cochineal); }

.btn--small { padding: 0.35rem 0.7rem; font-size: 0.85rem; }

/* ---------- Notices ---------- */

.notice {
  padding: 0.85rem 1rem;
  border-radius: 4px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  font-size: 0.95rem;
}

.notice--error { border-color: var(--cochineal); color: var(--cochineal); }
.notice--good { border-color: var(--indigo); color: var(--indigo); }

/*
  Each status carries its own colour, and the badge tints itself from that colour so
  the state reads across a table at a glance rather than only in the word.
*/
.badge {
  display: inline-block;
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 0.2rem 0.5rem;
  border-radius: 3px;
  border: 1px solid color-mix(in srgb, currentColor 45%, transparent);
  background: color-mix(in srgb, currentColor 12%, transparent);
}

.badge--full { color: var(--cochineal); }
.badge--open { color: var(--indigo); }
.badge--published { color: var(--weld-green); }
.badge--draft { color: var(--ink-faint); }
.badge--cancelled { color: var(--cochineal); }

/* ---------- Booking status page ---------- */

.statusCard {
  max-width: 34rem;
  margin-inline: auto;
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.4rem, 4vw, 2.2rem);
}

.statusCard__rule {
  height: 12px;
  margin: -1px -1px 1.5rem;
  border-radius: var(--radius) var(--radius) 0 0;
  background-image: var(--stitch-band);
  background-repeat: repeat-x;
  background-size: 14px 12px;
}

.defList {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.35rem 1.25rem;
  margin: 0;
  font-size: 0.95rem;
}
.defList dt { color: var(--ink-faint); }
.defList dd { margin: 0; }

/* Native disclosure, so confirming a cancellation needs no JavaScript. */
.disclosure { align-self: start; }
.disclosure summary { display: inline-flex; list-style: none; }
.disclosure summary::-webkit-details-marker { display: none; }
.disclosure[open] summary { border-color: var(--ink); }
.disclosure__body { margin-top: 0.75rem; }

