/* ============================================================
   Components — header, nav, footer. Mobile-first.
   ============================================================ */

/* ---- Header ---- */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--ink);
  color: var(--base);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  min-height: 60px;
}

/* The brand is now an image, so the typography this rule used to carry is gone
   with the text it styled — its only remaining child is the .visually-hidden
   name, which no font can reach. Layout and hit area only. */
.site-header__brand {
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
}

/* Sized off HEIGHT with width:auto, never the other way round: the lockup is
   1.49:1, so pinning width lets a narrow viewport decide how tall the header
   gets. block kills the inline-descender gap that would otherwise push the logo
   off the header's optical centre.

   THE HEIGHTS ROSE WITH THE ARTWORK, AND THAT IS ARITHMETIC RATHER THAN TASTE.
   The previous wordmark was one line at 2.24:1; this one stacks Fit / ROOM /
   gym experience / by Myrtw Mii beside the lion. Rendered at a common 1000px
   and the R of ROOM cropped out of each, its cap height is 341px in the old and
   246px in the new — 34% of the box against 25% — so holding 34/40 would have
   shrunk the readable half of the brand by a third while the box on the page
   stayed exactly as big. 46/54 is that 1.39 rounded, and puts ROOM back within
   0.3px of where it was. The lockup is also NARROWER at those heights than the
   old one was at 34/40 (68px against 76px), so the phone header gained nothing
   to accommodate and the sticky bar measured 60px tall before and after. */
.site-header__logo {
  display: block;
  height: 46px;
  width: auto;
}

@media (min-width: 768px) {
  .site-header__logo { height: 54px; }
}

.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
}

/* Both, not just the button: the <form> around the signed-in sign-out is a flex
   child of .site-header__actions in its own right, so hiding only its button
   would leave an empty box holding the gap open below 1024px. */
.site-header__login,
.site-header__signout { display: none; }
.site-header__signout { margin: 0; }

.lang-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding-inline: var(--space-2xs);
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--base);
  text-decoration: none;
  border-radius: var(--radius-m);
}
.lang-toggle:hover { color: var(--ember); }

/* ---- Mobile nav drawer ----
   Everything here is gated on html.js, because the drawer only exists where
   something can reopen it. Without JS the nav renders open and the button that
   would toggle it is hidden — a control that cannot do anything is worse than
   no control (WCAG 4.1.2), and the links behind it must stay reachable. */
.nav-toggle { display: none; }

.js .nav-toggle {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding: var(--space-2xs);
  background: transparent;
  border: 0;
  cursor: pointer;
}
.nav-toggle__bar {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--base);
  transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
/* Counting starts at 2, not 1: the button's first child is the visually-hidden
   accessible name, so the three bars are children 2, 3 and 4. :nth-child() counts
   every sibling, not just the ones matching the class, so 1/2/3 here would skew
   the X by one bar and leave the last one untransformed. 6px = one 2px bar plus
   the 4px gap, so the outer bars meet in the middle. */
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(2) { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(3) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(4) { transform: translateY(-6px) rotate(-45deg); }

.site-nav {
  position: fixed;
  inset: 60px 0 auto 0;
  background: var(--ink);
  border-top: 1px solid rgba(247, 246, 244, 0.12);
  z-index: var(--z-drawer);
}

/* Closed by default only when JS is available to reopen it. */
.js .site-nav { display: none; }
.js .site-nav[data-open="true"] { display: block; }

.site-nav__list {
  list-style: none;
  margin: 0;
  padding: var(--space-2xs) var(--container-pad) var(--space-m);
}

.site-nav__link {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  padding-block: var(--space-2xs);
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--base);
  text-decoration: none;
  border-bottom: 1px solid rgba(247, 246, 244, 0.1);
}
.site-nav__link:hover { color: var(--ember); }

/* ---- Account submenu ----
   The seven account screens plus Αποσύνδεση, nested under «Ο λογαριασμός μου»
   since 2026-09-09 — they were an in-page strip on every account page until
   then (pages/account/_nav.php, deleted).

   Mobile-first like the block above: here it is an indented list inside the
   drawer, and the @media block at the foot of this file turns it into a
   dropdown. The panel repeats the drawer's own --ink ground and hairline rather
   than inventing a second panel style.

   COLOUR AND BACKGROUND ARE SET TOGETHER IN EACH RULE BELOW, deliberately.
   a11y_test's sweep only resolves a rule that declares both as var(--token) —
   the gap that let 3c ship a 1.42:1 badge and landing AR a 1.00:1 ghost button
   on a dark section, each correct in its own rule and wrong on the page. Split
   across two rules these pairs would be invisible to it. */

.site-nav__item { list-style: none; }

.site-nav__summary {
  /* .site-nav__link is on the same element and supplies the type, the colour
     and the 44px floor. This adds only what a <summary> needs. */
  cursor: pointer;
  justify-content: space-between;
  gap: var(--space-2xs);
  /* The disclosure triangle, twice: Firefox and everything else read the first,
     Safari needs the pseudo-element. Removed because ::after below draws a
     chevron that can rotate, which the native marker cannot be made to do. */
  list-style: none;
}
.site-nav__summary::-webkit-details-marker { display: none; }

.site-nav__summary::after {
  content: "";
  width: 0.45em;
  height: 0.45em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-0.15em) rotate(45deg);
  transition: transform var(--dur-fast) var(--ease-out);
}
.site-nav__acct[open] > .site-nav__summary::after {
  transform: translateY(0.1em) rotate(-135deg);
}

/* 5.37:1 on --ink, the same pair .site-nav__link:hover has used since phase 1. */
.site-nav__summary.is-current {
  color: var(--ember);
  background: var(--ink);
}

.site-nav__sub {
  list-style: none;
  margin: 0;
  padding: var(--space-2xs) 0 var(--space-2xs) var(--space-s);
  color: var(--base);
  background: var(--ink);
}

/* One rule for the seven links and the sign-out button, so the row that submits
   a form cannot drift from the rows that follow one. The button resets are what
   a <button> needs to stop looking like one. */
.site-nav__sub-link {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: var(--tap-min);
  padding-block: var(--space-3xs);
  padding-inline: 0;
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 400;
  letter-spacing: 0;
  text-align: start;
  text-transform: none;
  text-decoration: none;
  color: var(--base);
  background: var(--ink);
  border: 0;
  cursor: pointer;
}
.site-nav__sub-link:hover { color: var(--ember); }

.site-nav__sub-link.is-current {
  color: var(--ember);
  background: var(--ink);
}

/* Αποσύνδεση is an action, not a destination, and the hairline says so without
   colour doing the work — WCAG 1.4.1: on this ground the only colour that would
   carry it is --ember, which .is-current above already means. */
.site-nav__signout {
  margin: 0;
  border-top: 1px solid rgba(247, 246, 244, 0.12);
}

@media (min-width: 1024px) {
  .nav-toggle,
  .js .nav-toggle { display: none; }
  /* Both halves come back, and the form is why this is two selectors and not
     one: the button carries .site-header__login, but its <form> wrapper is what
     .site-header__actions actually lays out. Restoring only the button leaves it
     inside a `display: none` parent — measured at 1280px, an invisible sign-out
     with every DOM assertion about it passing. */
  .site-header__login { display: inline-flex; }
  .site-header__signout { display: block; }

  .site-nav,
  .js .site-nav {
    display: block;
    position: static;
    background: transparent;
    border: 0;
  }
  .site-nav__list { display: flex; gap: var(--space-m); padding: 0; }
  .site-nav__link {
    font-size: var(--step--1);
    letter-spacing: 0.12em;
    border: 0;
    padding-inline: 0;
  }

  /* Σύνδεση in the drawer exists because .site-header__login is hidden below
     this breakpoint. Above it the button is back, so the row would be the same
     destination twice in one header. */
  .site-nav__item--mobile { display: none; }

  /* The dropdown. .site-header sets no overflow, so an absolutely-positioned
     panel escapes it; the header is z-index --z-header, which already puts the
     whole bar above the page. */
  .site-nav__item--account { position: relative; }

  .site-nav__sub {
    position: absolute;
    inset-inline-start: 0;
    top: calc(100% + var(--space-2xs));
    min-width: 14rem;
    padding: var(--space-2xs) var(--space-s);
    border: 1px solid rgba(247, 246, 244, 0.12);
    border-radius: var(--radius-m);
    z-index: var(--z-drawer);
  }

  .site-nav__sub-link { font-size: var(--step--1); }
}

/* ---- Footer ---- */
/* WHITE SINCE 2026-08-28, and that inverted every colour in this block.
   Measured against #FFFFFF with a11y_test's own helper before anything moved:
   --brass 2.42:1, the old tagline grey 2.79:1, the old address grey 2.06:1 and
   --base links 1.08:1 all FAIL on white — four of the six colours here were
   chosen for a dark ground and none of them survive the flip. Their
   replacements are --ember-text 4.90:1, --muted 5.05:1 and --ink 18.54:1.
   Do not reintroduce --brass or --ember for TEXT here; both are below AA on
   this background (--ember is 3.45:1, large-text only). */
.site-footer {
  background: var(--surface);
  color: var(--ink);
  border-top: 1px solid var(--border);
  padding-block: var(--space-xl) var(--space-m);
  /*
   * NO TOP MARGIN ANY MORE. There was 6rem of it, and it earned its keep while
   * the footer was --deep: it held a dark block away from light content so the
   * two did not read as one slab. A white footer against the dark About section
   * needs the opposite — the margin rendered as a band of --base between the
   * dark section and the white footer, with the border-top line floating in the
   * middle of it. The border alone now does the separating, and it only shows
   * where the content above is also light. */
  margin-top: 0;
}

.site-footer__inner { display: grid; gap: var(--space-l); }

/* An image now, like the header's. Sized off height with width:auto — the
   lockup is 1.49:1, so pinning width would let a narrow viewport decide how
   tall it gets. Larger than the header's 54px because this is the sign-off
   rather than a nav item, and the byline under the script line only starts
   reading properly above ~70px. */
/* THE BLACK FILE, NOT THE WHITE ONE INVERTED — AND THE FILTER THAT USED TO BE
   HERE IS DELETED RATHER THAN DISABLED. The fill lives in an internal <style>
   block inside the SVG, public pages ship `style-src 'self'` with no
   'unsafe-inline' (constraint 5), and an <img>'s paths are out of this
   document's reach either way — so a white logo on the white footer landing C
   left behind could only be corrected by inverting the whole image. It was, and
   it rendered #000. The owner supplied both fills on 2026-09-09, so the ground
   picks the file now: this one names logo-fitroom-black.svg and renders
   #231f20, the colour the artwork actually specifies. The two files are
   byte-identical apart from that one declaration, which is exactly why they
   must be replaced as a PAIR — a new white logo with the old black one still on
   disk is a footer nobody can see, reported by nothing. */
.site-footer__logo {
  display: block;
  height: 72px;
  width: auto;
}

@media (min-width: 768px) {
  .site-footer__logo { height: 88px; }
}
.site-footer__tagline { color: var(--muted); margin-top: var(--space-2xs); }

.site-footer__heading {
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ember-text);
  margin-bottom: var(--space-2xs);
}

.site-footer__address { font-style: normal; line-height: var(--lh-body); color: var(--muted); }

/* Who to ask and what for, beside the second number — the same pairing the
   merged contact section used, at the same weight relative to it. */
/* Its OWN line, unlike the inline version the contact section used. There the
   note sat beside a number in a half-width block and had room; here the column
   is ~200px at desktop, and an inline span breaks mid-phrase — "690 657 0560
   coach" on one line and "Σοφία · Reformer class" on the next, which reads as
   a rendering fault rather than as a label. Seen in the browser, not in a
   test: no assertion can tell a good wrap from a bad one. */
.site-footer__note  { display: block; color: var(--muted); font-size: var(--step--1); }
.site-footer__hours { color: var(--muted); margin: 0; line-height: var(--lh-body); }
.site-footer__maps  { display: inline-flex; align-items: center; min-height: var(--tap-min);
                      font-weight: 600; margin-top: var(--space-3xs); }

/* The glyph sits before its label inside a link that is already inline-flex
   with a 44px tap target, so it needs no sizing of its own beyond flex: none —
   without that it is the first thing a narrow column squashes. No colour here:
   fill="currentColor" on the path is what makes it follow the link, hover
   included. */
.social-icon { flex: none; margin-right: var(--space-2xs); }

.site-footer__links { list-style: none; margin: 0; padding: 0; }
.site-footer__links li { margin-bottom: var(--space-3xs); }
.site-footer a { color: var(--ink); text-decoration: none; }
.site-footer a:hover { color: var(--ember-text); text-decoration: underline; }
.site-footer__links a,
.site-footer__address a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
}

.site-footer__legal {
  border-top: 1px solid var(--border);
  margin-top: var(--space-l);
  padding-top: var(--space-m);
  /*
   * --muted, AND THE REASON IS THE EXACT REVERSE OF WHAT IT WAS.
   *
   * This line used to read: "Not #6E6E76 (--muted): on --deep that measures
   * 3.82:1 … --muted is tuned for light backgrounds; on the dark footer it is
   * not readable enough." All of that was true, and the footer is light now, so
   * the tuning it described is finally the right tuning: --muted measures
   * 5.05:1 on --surface, while #9A9AA2 — correct at 6.90:1 on --deep — drops to
   * 2.79:1 here and fails outright. The old comment is kept above in quotes on
   * purpose: it records why the hex was ever there, which is the thing a future
   * reader would otherwise have to rediscover before daring to change it.
   */
  color: var(--muted);
  font-size: var(--step--1);

  /* Two lines since the build credit joined the copyright: side by side where
     there is room, stacked where there is not. flex-wrap does the whole job —
     no breakpoint, because the point at which they stop fitting depends on the
     translated string's length rather than on a device width. This element is
     also a .container, which sets only width/max-width/margin/padding, so
     giving it a display here takes nothing away. */
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 0 var(--space-m);
}

.site-footer__legal p { margin: 0; }

/* UNDERLINED, unlike every other footer link, and deliberately. The others sit
   in lists where being a link is obvious from position; this one is a word
   inside a sentence, where brightness alone would be colour carrying meaning
   (WCAG 1.4.1). The tap target matches .site-footer__links a — on its own line
   a 44px-tall inline-flex costs nothing. */
.site-footer__credit a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  text-decoration: underline;
}

@media (min-width: 640px) {
  .site-footer__inner { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  /* Five columns since the contact section merged in and brought opening
     hours with it. The brand keeps the widest track and the two link lists the
     narrowest; the two prose columns sit between them. */
  .site-footer__inner { grid-template-columns: 1.6fr 1.3fr 1.2fr 1fr 1fr; gap: var(--space-l); }
}

/* ---- Offer bar --------------------------------------------------------
   Sits above the site header. --ember-text on white is the pair a11y_test
   already holds at AA (4.61:1); --ember is the brand orange and fails at
   3.45:1, which is exactly why the primary button uses the darker token too. */
.offer-bar {
  background: var(--ember-text);
  color: #FFFFFF;
}
.offer-bar__inner {
  display: flex; align-items: center; gap: var(--space-2xs);
  min-height: 44px;
  padding-block: var(--space-3xs);
}
.offer-bar__text {
  margin: 0; flex: 1 1 auto;
  font-size: var(--step--1); font-weight: 600;
}
.offer-bar__deadline {
  flex: 0 0 auto;
  font-size: var(--step--1); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.offer-bar__dismiss { flex: 0 0 auto; margin: 0; }
.offer-bar__close {
  display: flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px;       /* the tap floor, like every control */
  padding: 0; border: 0; background: transparent;
  color: inherit; font-size: 1.25rem; line-height: 1; cursor: pointer;
}
.offer-bar__close:hover { opacity: 0.75; }

/* ---- Reformer dialog ------------------------------------------------------
 *
 * The phone-only slots on the timetable open this instead of a booking page.
 * A native <dialog>: focus trapping, Escape, inertness of the page behind and
 * the ::backdrop pseudo-element are all the element's own behaviour, so none of
 * them is reimplemented here. main.js only calls showModal().
 *
 * NO WIDTH ON THE <dialog> ITSELF, only a max-width. The element is the full
 * viewport when open and the visible card is .phone-modal__inner — that split
 * is what lets main.js treat "clicked the element but not the inner" as a
 * backdrop click without measuring anything.
 */
.phone-modal {
  border: 0;
  padding: 0;
  max-width: min(28rem, calc(100vw - 2 * var(--space-s)));
  border-radius: var(--radius-l);
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 12px 40px rgb(14 14 16 / 0.25);
}
.phone-modal::backdrop { background: rgb(14 14 16 / 0.6); }

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

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

/* The class the visitor actually clicked, so the dialog answers the question
   that was asked rather than a general one. Hidden until main.js fills it. */
.phone-modal__class  { margin: 0; font-weight: 700; color: var(--ember-text); }

.phone-modal__body   { margin: 0; line-height: var(--lh-body); }
.phone-modal__call   { justify-self: stretch; text-align: center; }
.phone-modal__note   { margin: 0; font-size: var(--step--1); color: var(--muted); }
.phone-modal__close  { margin: 0; }

/* The timetable trigger. Styled as the other availability labels are, with the
   ember rule that marks it as an action rather than a state. */
.slot__spots--phone,
.agenda-row__spots--phone { border-bottom: 1px dotted currentColor; }

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

/* Guarded by no-preference above rather than reversed out under `reduce`: a
   keyframe that has never been attached needs no undoing, and the pattern
   cannot be defeated by specificity the way an `!important` override can. */
@keyframes phone-modal-in {
  from { opacity: 0; transform: translateY(0.75rem) scale(0.98); }
  to   { opacity: 1; transform: none; }
}
@keyframes phone-backdrop-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
