/* Sections — populated in Tasks 8-11. */

/* ---- Hero ---------------------------------------------------------------
   svh rather than vh: on mobile, vh is sized to the *expanded* URL bar, so a
   100vh hero jumps as the bar collapses. svh is the small-viewport unit.
   ------------------------------------------------------------------------ */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  padding-block: var(--space-2xl) var(--space-xl);
  background: var(--deep);
  color: var(--base);
  overflow: hidden;
}

.hero__media { position: absolute; inset: 0; }
.hero__img { width: 100%; height: 100%; object-fit: cover; }
.hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(14, 14, 16, 0.55) 0%, rgba(14, 14, 16, 0.85) 100%);
}

.hero__inner { position: relative; z-index: 1; }
.hero__kicker { color: var(--base); opacity: 0.85; }

.hero__title {
  font-size: var(--step-5);
  line-height: var(--lh-tight);
  text-transform: uppercase;
  margin-bottom: var(--space-s);
  max-width: 16ch;
}

.hero__sub {
  font-size: var(--step-1);
  color: #D6D6DC;
  max-width: 46ch;
  margin-bottom: var(--space-m);
}

.hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-2xs); }

.hero__scroll {
  position: absolute;
  bottom: var(--space-m);
  left: 50%;
  transform: translateX(-50%);
  z-index: 1;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  min-height: var(--tap-min);
  min-width: var(--tap-min);
}
.hero__scroll-line {
  width: 1px;
  height: 32px;
  background: linear-gradient(180deg, transparent, var(--base));
}

@media (min-width: 1024px) {
  .hero { align-items: center; }
  .hero__title { max-width: 14ch; }
}

/* ---- The footer's one photograph: the door ---- */

/*
 * Cropped to a wide strip rather than shown at its native 3:4, because the
 * footer column is narrow and a full-height portrait would push the hours and
 * the social links a screen and a half down on a phone. The crop keeps the lit
 * signage, which is the whole reason the photo is here.
 */
.site-footer__photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  object-position: 50% 35%;
  margin-top: var(--space-s);
  border-radius: var(--radius-m);
}

/* ---- The coaches ----------------------------------------------------------
 *
 * Sits inside .about, which is .section--dark: the ground is --ink and the
 * text --base. Secondary lines take the same #B4B4BC literal .about__text
 * already uses on this exact background rather than a new token, because
 * a11y_test's colour sweep only resolves var(--token) pairs and a fresh token
 * invented here would carry a contrast nothing measures — the trap that put a
 * 1.42:1 badge live in 3c.
 */
.coaches { margin-top: var(--space-l); }

.coaches__title {
  font-size: var(--step-1);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--brass);
  margin-bottom: var(--space-m);
}

.coaches__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-l);
  grid-template-columns: 1fr;
  justify-items: center;
  text-align: center;
}

/*
 * A COLUMN, NOT A GRID, SO THE TWO CARDS STAY LEVEL. The grid row stretches
 * both cards to the taller one, and a stretched grid shares the spare height
 * among its auto rows: when Μυρτώ's role wrapped to two lines at 1440 in
 * English, Σοφία's name sat 5.6px low and her button 5.6px high. Here the
 * spare height goes to the role alone (flex-grow below), so photo, name and
 * role start on the same lines in both cards and the buttons share the bottom
 * one. Without script it also stops an opened <details> from spreading the
 * OTHER card's photo, name and role ~1,300px apart, as the grid did.
 */
.coach {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2xs);
  max-width: 22rem;
}

/*
 * THE CIRCLE IS DRAWN HERE, NOT BAKED INTO THE FILE. A pre-rounded PNG needs an
 * alpha channel, and transparency in a photograph costs several times the bytes
 * of the same photograph without it — for a shape one declaration already
 * makes. The files are square, so 50% is a circle and object-fit only matters
 * if one is ever replaced with a non-square crop.
 */
.coach__photo {
  width: min(11rem, 55vw);
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 50%;
  border: 3px solid var(--brass);
  margin-bottom: var(--space-2xs);
}

.coach__name {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 900;
  letter-spacing: 0.04em;
  color: var(--base);
  margin: 0;
}

.coach__role { font-size: var(--step--1); color: #B4B4BC; margin: 0; flex-grow: 1; }

.coach__cv { margin-top: var(--space-2xs); }

/*
 * ONE CONTROL IS VISIBLE, NEVER BOTH. JS_ENABLE_SNIPPET puts `.js` on <html>,
 * so the button — which needs script to do anything — is hidden until that
 * class exists, and the <details>, which needs none, is hidden once it does.
 * `.no-dialog` is main.js's own escape hatch: an engine with `.js` but without
 * <dialog>.showModal() would otherwise show a button that does nothing, so the
 * script puts that class on and this rule hands the page back to <details>.
 */
.coach__cv { display: none; }
.js .coach__cv { display: inline-flex; }
.js .coach__details { display: none; }
.js.no-dialog .coach__cv { display: none; }
.js.no-dialog .coach__details { display: block; }

.coach__details { max-width: 32rem; text-align: left; }

.coach__summary {
  cursor: pointer;
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brass);
  text-align: center;
}

.coach__bio { margin-top: var(--space-s); color: #B4B4BC; line-height: var(--lh-body); }
.coach__bio p + p { margin-top: var(--space-s); }

@media (min-width: 640px) {
  .coaches__grid { grid-template-columns: repeat(2, 1fr); }
}

/* ---- The coach biography dialog -------------------------------------------
 *
 * The same shape as the Reformer dialog in components.css and for the same
 * reasons — no width on the element, only a max-width, so main.js can read
 * "clicked the element but not the inner card" as a backdrop click without
 * measuring anything. It is taller than that one, because a biography is
 * several paragraphs, so the card scrolls rather than the page behind it.
 */
.coach-modal {
  border: 0;
  padding: 0;
  max-width: min(34rem, calc(100vw - 2 * var(--space-s)));
  max-height: min(85vh, 48rem);
  border-radius: var(--radius-l);
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 12px 40px rgb(14 14 16 / 0.25);
  /*
   * The dialog is rendered inside .coaches__grid, whose text-align: center is
   * for the cards. Inherited here it centred the biography's paragraphs under a
   * title, role and Close button that sit left — the <details> fallback has the
   * same override for the same reason.
   */
  text-align: left;
}
.coach-modal::backdrop { background: rgb(14 14 16 / 0.6); }

.coach-modal__inner {
  padding: var(--space-m);
  display: grid;
  gap: var(--space-s);
  justify-items: start;
}

.coach-modal__title {
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: var(--lh-heading);
  margin: 0;
}

.coach-modal__role { margin: 0; font-weight: 700; color: var(--ember-text); }

.coach-modal__body { margin: 0; line-height: var(--lh-body); }
.coach-modal__body p { margin: 0; }
.coach-modal__body p + p { margin-top: var(--space-s); }

.coach-modal__close { margin: 0; }

@media (prefers-reduced-motion: no-preference) {
  .coach-modal[open]           { animation: phone-modal-in var(--dur-base) var(--ease-out); }
  .coach-modal[open]::backdrop { animation: phone-backdrop-in var(--dur-base) var(--ease-out); }
}

/* ---- Ο χώρος (studio photographs) ---- */

/*
 * NO `display: none` ANYWHERE IN THIS BLOCK, and that is a rule this project
 * pays for when it is broken: .admin-table is hidden below 768px as HALF a
 * mechanism, and three 4b screens shipped invisible on every phone because a
 * DOM assertion cannot see a media query. This grid reflows instead of hiding —
 * one column on a phone, two on a tablet, three on a desktop — so every photo
 * is reachable at every width.
 */
.studio { background: var(--surface); }

.studio__grid {
  list-style: none;
  margin: var(--space-m) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-s);
  grid-template-columns: 1fr;
}

.studio__item {
  margin: 0;
  overflow: hidden;
  border-radius: var(--radius-l);
  border: var(--border-thin);
  background: var(--base);
}

/*
 * The files are portrait 3:4 and the grid shows them 4:3 landscape, cropped
 * from the centre. aspect-ratio on the IMG rather than a padding box, so the
 * width/height attributes still reserve the right shape before the bytes land
 * and the section does not shove the schedule down the page as it loads.
 */
.studio__img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform var(--dur-base) var(--ease-out);
}

/*
 * Attached under `no-preference` rather than reversed out under `reduce`,
 * matching the phone-modal rule in components.css: a transform that was never
 * applied needs no undoing, and somebody who has asked their system for less
 * motion should not have to rely on a second rule remembering to switch this
 * one off.
 */
@media (prefers-reduced-motion: no-preference) {
  .studio__item:hover .studio__img { transform: scale(1.03); }
}

@media (min-width: 640px) {
  .studio__grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .studio__grid { grid-template-columns: repeat(3, 1fr); }
}

/* ---- Services ---- */
.services { background: var(--surface); }

.services__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-s);
}

.service-card {
  position: relative;
  background: var(--base);
  border: var(--border-thin);
  border-radius: var(--radius-l);
  padding: var(--space-m);
  transition: border-color var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.service-card:hover { border-color: var(--ember); transform: translateY(-2px); }

.service-card__num {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 900;
  color: var(--ember-text);
  margin-bottom: var(--space-2xs);
}

.service-card__title {
  font-size: var(--step-1);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  margin-bottom: var(--space-2xs);
}

.service-card__body { color: var(--muted); margin: 0; }

.services__closing {
  margin-top: var(--space-l);
  padding-top: var(--space-m);
  border-top: 2px solid var(--ink);
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  line-height: var(--lh-heading);
  max-width: 44ch;
}

@media (min-width: 640px) {
  .services__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .services__grid { grid-template-columns: repeat(3, 1fr); gap: var(--space-m); }
  .services__closing { margin-top: var(--space-xl); font-size: var(--step-3); }
}

/* ---- Schedule ---- */
.schedule { background: var(--base); }

/* Desktop week grid */
.schedule-table { width: 100%; border-collapse: separate; border-spacing: 3px; }
.schedule-table th[scope="col"] {
  background: var(--ink);
  color: var(--base);
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 900;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  padding: var(--space-2xs);
  border-radius: var(--radius-m);
}
.schedule-table__time {
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 900;
  color: var(--ink);
  text-align: center;
  width: 64px;
  vertical-align: middle;
}
.schedule-table__cell {
  background: var(--surface);
  border-radius: var(--radius-m);
  padding: 3px;
  vertical-align: top;
}

.slot {
  display: flex;
  flex-direction: column;
  gap: 2px;
  height: 100%;
  min-height: 68px;
  justify-content: center;
  padding: var(--space-2xs);
  border-radius: var(--radius-m);
  border-left: 4px solid var(--slot-color, var(--ink));
  background: var(--base);
}
.slot--cross   { --slot-color: var(--family-cross); }
.slot--lower   { --slot-color: var(--family-lower); }
.slot--tabata  { --slot-color: var(--family-tabata); }
.slot--athlete { --slot-color: var(--family-athlete); }
.slot--best    { --slot-color: var(--family-best); }
.slot--pilates { --slot-color: var(--family-pilates); }
.slot--pt      { --slot-color: var(--family-pt); }

.slot__name  { font-weight: 700; font-size: var(--step--1); line-height: 1.25; }
.slot__time  { font-family: var(--font-display); font-size: var(--step--1); color: var(--muted); }
.slot__spots { font-size: var(--step--1); font-weight: 600; color: var(--success); }
.slot[data-state="few"]  .slot__spots { color: var(--ember-text); }
.slot[data-state="full"] .slot__spots { color: var(--muted); }
/* NOT the --success green the other states default to. Green on this grid means
   "there is a seat, take it"; a Reformer class has no seat here at all — it is
   arranged with coach Σοφία — so it reads as information rather than as
   availability. The dotted underline in components.css is what keeps it
   legible as something to click. */
.slot[data-state="phone"] .slot__spots { color: var(--ink); }

/* Mobile agenda */
.agenda-day + .agenda-day { margin-top: var(--space-m); }
.agenda-day__title {
  font-size: var(--step-1);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding-bottom: var(--space-3xs);
  border-bottom: 2px solid var(--ink);
  margin-bottom: var(--space-2xs);
}
.agenda-day__list { list-style: none; margin: 0; padding: 0; }

.agenda-row {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-height: var(--tap-min);
  padding: var(--space-2xs) 0;
  border-bottom: var(--border-thin);
}
.agenda-row__time { font-family: var(--font-display); font-weight: 900; width: 3.4rem; flex: none; }
.agenda-row__body { flex: 1; display: flex; flex-direction: column; }
.agenda-row__name { font-weight: 600; line-height: 1.3; }
.agenda-row__meta { font-size: var(--step--1); color: var(--muted); }
.agenda-row__spots {
  flex: none;
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--success);
  white-space: nowrap;
}
.agenda-row[data-state="few"]  .agenda-row__spots { color: var(--ember-text); }
.agenda-row[data-state="full"] .agenda-row__spots { color: var(--muted); }
/* See the grid rule above — green would promise a seat this system never holds. */
.agenda-row[data-state="phone"] .agenda-row__spots { color: var(--ink); }

/* Legend — decorative reinforcement, never the carrier of meaning */
.schedule__legend {
  list-style: none;
  margin: var(--space-m) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
}
.schedule__legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  font-size: var(--step--1);
  color: var(--muted);
}
.schedule__swatch { width: 12px; height: 12px; border-radius: 2px; background: var(--ink); }
.schedule__swatch--cross   { background: var(--family-cross); }
.schedule__swatch--lower   { background: var(--family-lower); }
.schedule__swatch--tabata  { background: var(--family-tabata); }
.schedule__swatch--athlete { background: var(--family-athlete); }
.schedule__swatch--best    { background: var(--family-best); }
.schedule__swatch--pilates { background: var(--family-pilates); }
.schedule__swatch--pt      { background: var(--family-pt); }

/* Closures, stated above the timetable because the timetable is a weekly
   pattern and cannot show a date (see the comment in schedule.php). Everything
   here is --ink on the section's --base: --muted would pass at 4.68:1, but this
   is the one block on the page a member must not skim past, and a thin margin
   on the thing that says "we are shut" is the wrong place to save contrast. */
.schedule__closures {
  margin: 0 0 var(--space-m);
  padding: var(--space-s) var(--space-m);
  border-left: 3px solid var(--ember);
}
.schedule__closures-title {
  margin: 0 0 var(--space-3xs);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ember-text);
}
.schedule__closures-list { list-style: none; margin: 0; padding: 0; }
.schedule__closures-list li { margin-top: var(--space-3xs); }
.schedule__closures-reason { margin-left: var(--space-2xs); }

/* ---- What a booking did ----
   pages/account/book.php returns every outcome to the timetable (2026-09-15),
   and this is where the sentence lands. --surface on this section's --base
   ground, with an inline-start border so a success has an edge too: --ember for
   a refusal, as .account__notice--error has it, and --sand otherwise, the
   account area's info colour. No rule sets a text colour, so the text keeps
   --ink and no new pair enters a11y_test's sweep. */
.schedule__flash { margin: 0 0 var(--space-m); }
.schedule__notice {
  margin: 0 0 var(--space-2xs);
  padding: var(--space-s);
  border-radius: var(--radius-m);
  background: var(--surface);
  border-inline-start: 3px solid var(--sand);
}
.schedule__notice--error,
.schedule__notice--warn { border-inline-start-color: var(--ember); }

/* ---- Week navigation ----
   The control that turns a weekly pattern into a quarter of bookable classes.
   Rendered only when the booking horizon reaches at least one whole week ahead;
   see pages/sections/schedule.php. */
.schedule__weeks {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2xs);
  margin: 0 0 var(--space-m);
}
.schedule__weeks-label {
  flex: 1 1 12rem;
  margin: 0;
  text-align: center;
}
.schedule__weeks-hint {
  display: block;
  margin-top: var(--space-3xs);
  font-size: var(--step--1);
  color: var(--muted);
}
.schedule__weeks-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  padding: var(--space-2xs) var(--space-s);
  border: var(--border-thin);
  border-radius: var(--radius-pill);
  font-size: var(--step--1);
  text-decoration: none;
  color: var(--ink);
  background: var(--surface);
}
.schedule__weeks-btn:hover,
.schedule__weeks-btn:focus-visible { border-color: var(--ember); }
/* The invisible counterweight that keeps the label centred when only one arrow
   exists. It keeps its padding, so it occupies roughly an arrow's width. */
.schedule__weeks-btn--empty {
  border-color: transparent;
  background: none;
  pointer-events: none;
}

/* The date beside a weekday, on a dated week only. Regular weight so the day
   name stays the heading and the date reads as its qualifier.

   --base AT 0.75, NOT --muted, AND THE GROUND IS WHY. This header is the one
   dark strip in the section: th[scope="col"] sets background:var(--ink) with
   color:var(--base). --muted (#6E6E76) is the section's secondary text colour
   everywhere else and measures 4.68:1 on the --base body — but 3.67:1 on --ink,
   which fails. Dimming --base instead keeps the same visual role at 9.89:1.

   a11y_test COULD NOT HAVE CAUGHT THIS, and that is the lesson rather than the
   colour. Its stylesheet sweep resolves rules that set BOTH color and
   background; this rule sets only color and inherits its ground from a parent
   selector, so it is invisible to the sweep exactly as the 1.42:1 badge of 3c
   was. Constraint 31's shape, one property over: when you write a colour, look
   up the chain for what it will sit on. */
.schedule-table__date {
  display: block;
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--base);
  opacity: 0.75;
}
.agenda-day__date {
  margin-left: var(--space-2xs);
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--muted);
  /* The day heading is uppercase with 0.08em tracking, which a date INHERITS —
     "31/8" rendered as "3 1 / 8". Tracking is for capitals; digits and a slash
     are neither uppercase nor helped by it. */
  letter-spacing: normal;
  text-transform: none;
}

.schedule__cta { margin-top: var(--space-l); }

/* ---- About ---- */
.about__grid { display: grid; gap: var(--space-l); }
.about__lede { font-size: var(--step-1); color: var(--base); }
.about__text { color: #B4B4BC; }

.about__stats { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-s); }
.stat { border-left: 3px solid var(--ember); padding-left: var(--space-s); }
.stat__value {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: 900;
  line-height: 1;
  color: var(--base);
}
.stat__label {
  display: block;
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brass);
  margin-top: var(--space-3xs);
}

@media (min-width: 640px) {
  /* auto-fit, NOT repeat(3, ...): the number of stats is data, not layout.
     about.php renders only the tiles with a non-empty value, and that count
     has moved twice in two days — three tiles, then two when the owner dropped
     the group-size figure on 2026-08-28, then three again on 2026-08-29 when
     the slot was reused for Reformer hours. A hardcoded 3 leaves a dead column
     on the right between 640px and 1023px — no error, nothing missing from the
     DOM, just a lopsided row that only a screenshot at that width shows.
     auto-fit collapses the empty tracks, so 2 tiles share the row and a
     restored 3rd still fits. That this rule needed no edit across both changes
     is the point of writing it this way.
     The 1024px block below re-declares grid-template-columns, so this does
     not leak into the stacked layout. */
  .about__stats { grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); }
}
@media (min-width: 1024px) {
  .about__grid { grid-template-columns: 1.4fr 1fr; gap: var(--space-xl); align-items: center; }
  .about__stats { grid-template-columns: 1fr; gap: var(--space-m); }
}

/* ---- Contact ---- */
/* The section these rules styled was merged into the footer on 2026-08-28 and
   pages/sections/contact.php deleted with it; nothing rendered .contact* any
   more, so the rules went with the markup rather than sitting here as dead
   weight for the next reader to check against the DOM. The footer carries its
   own site-footer__* equivalents in components.css. */

/* ---- Legal / long-form pages ---- */
.legal { background: var(--surface); }
.legal__inner { max-width: 68ch; }
.legal__title { margin-bottom: var(--space-m); }
.legal__body h2 {
  font-size: var(--step-1);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-top: var(--space-l);
  margin-bottom: var(--space-2xs);
}
.legal__body p, .legal__body ul { color: var(--ink); }

/* Narrow screens: a 3-column policy table must scroll, not overflow the page. */
.legal__body table {
  width: 100%;
  border-collapse: collapse;
  margin-block: var(--space-s);
  display: block;
  overflow-x: auto;
}
.legal__body th, .legal__body td {
  text-align: left;
  padding: var(--space-2xs);
  border-bottom: var(--border-thin);
  font-size: var(--step--1);
}
.legal__body th {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
}
.legal__actions { display: flex; flex-wrap: wrap; gap: var(--space-2xs); margin-top: var(--space-m); }

@media (min-width: 640px) {
  .legal__body table { display: table; overflow-x: visible; }
}
