/* ============================================================
   Skye Yoga Penang — one stylesheet, no framework, no CDN.
   Order: font → tokens → reset → layout → nav → modal →
          typography → components → footer → small screens.
   Comments explain WHY a number exists, not what it does.
   ============================================================ */

/* ---------- font ---------- */

/* One variable-weight latin subset (32 KB) replaces four static weights and
   one third-party DNS lookup. Preloaded in <head> with crossorigin. */
@font-face {
  font-family: Outfit;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/outfit-latin.woff2") format("woff2");
}

/* ---------- design tokens ---------- */

:root {
  --clay:       #c2603c;   /* primary accent: h1, links, active nav, focus */
  --clay-dark:  #a24c2d;   /* accent hover only */
  --green:      #4f8a5b;   /* affirmative: "yes" cells, blockquote rule */
  --teal:       #2f6d78;   /* data emphasis: table headers, figures, levels */
  --ink:        #2c2a28;   /* body text — near-black, never #000 */
  --muted:      #6b6660;   /* secondary text, labels, footer */
  --rule:       #e3ddd5;   /* all 1px borders */
  --stripe:     #f7f3ee;   /* zebra rows, hover chips, note/quote fills */
  --page:       #fffdfa;   /* page and surface background */
  --max:        900px;     /* content measure */
  --banner-max: 1920px;    /* banner stops growing here */
}

/* Role discipline: clay = action and navigation state, teal = quantities and
   identifiers, green = success, red = negative, muted = labels. Never reuse
   the accent for data. #d24b3f (destructive / "no", modal close) and #ece6dd
   (table row hover) appear once each and are semantic, not brand, so they are
   deliberately not tokenised. */

/* ---------- reset and page frame ---------- */

*, *::before, *::after { box-sizing: border-box; }

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;   /* dvh handles collapsing mobile browser chrome; vh is the fallback */
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: Outfit, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 17px;      /* larger than the usual 16px — the reason long pages stay readable */
  line-height: 1.6;
}

.container {
  width: 100%;
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 20px;
}

/* ---------- masthead ---------- */

.masthead {
  position: relative;
  flex: 0 0 auto;              /* without this the flex column can compress the aspect box */
  aspect-ratio: 2313 / 1000;   /* the artwork's real pixel ratio — reserves exact height, zero CLS */
  width: 100%;
  max-width: var(--banner-max);
  margin-inline: auto;
  background-image: var(--banner);   /* set inline from PHP, with its mtime stamp */
  background-position: center top;
  background-repeat: no-repeat;
  background-size: 100% auto;
}

/* The banner is the only element tracking viewport width, and its height
   derives from that width. Uncapped, a 2560px ultrawide asks for 1107px of
   banner on a 1080px screen and pushes the menu off the bottom; capping at
   1920px and centring makes every display >= 1920px render identically. */

/* ---------- navigation ---------- */

.nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  align-items: center;   /* not stretch: each pill sizes from its own padding */
  padding: 20px 0 10px;
  margin-bottom: 30px;
  position: relative;   /* the submenu anchors to the BAR, not to its toggle */
}

.nav a,
.nav-group-toggle {
  display: inline-block;
  padding: 8px 16px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: 16px;
  /* Stated, not inherited: the body's 1.6 and a <button>'s UA "normal" differ,
     which is what made the pills disagree on height. Fixing it here keeps every
     pill — link, dropdown toggle and the filled booking one — at 8+24+8 = 40px. */
  line-height: 1.5;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .2s, color .2s;
}

.nav a:hover,
.nav-group-toggle:hover               { background: var(--stripe); color: var(--clay); }
.nav a[aria-current="page"],
.nav-group.is-active .nav-group-toggle { background: var(--clay); color: #fff; }

/* Active state is aria-current, not a .active class: the accessibility
   attribute IS the styling hook, so there is one source of truth and screen
   readers announce the state for free. */

/* Pull the menu up onto the lower part of the banner. The offset is a
   percentage so it resolves against viewport width and tracks the banner's
   height as it scales; -9.3% lands it on the cream fade the artwork ends in. */
/* The +15px is a fixed nudge on top of the proportional offset: it does not
   scale with the banner, so the menu keeps the same gap from the artwork's
   bottom edge at every width. */
@media screen and (min-width: 701px) {
  .nav { z-index: 1; margin-top: calc(-9.3% + 15px); }
}

/* Past the banner's cap the artwork stops growing, so the offset must stop
   too — otherwise the percentage keeps dragging the menu up into the wordmark
   on ultrawides. The 1920px literal must track --banner-max: media queries
   cannot read custom properties. */
@media screen and (min-width: 1920px) {
  .nav { margin-top: calc(var(--banner-max) * -0.093 + 15px); }
}

.nav-group { position: static; }   /* deliberately NOT a positioning context */

/* The submenu is centred under the whole nav bar rather than under its toggle:
   a toggle near the right edge would otherwise push a wide submenu off screen. */
.nav-menu {
  display: none;
  position: absolute;
  top: calc(100% + 4px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  width: max-content;              /* without this the links wrap into three rows */
  max-width: min(94vw, 1000px);
  padding: 6px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: var(--page);
  box-shadow: 0 6px 20px rgba(0,0,0,.12);
}

/* Three ways to open, one per input mode: click, mouse hover, keyboard focus. */
.nav-group-toggle[aria-expanded="true"] + .nav-menu,
.nav-group:hover .nav-menu,
.nav-group:focus-within .nav-menu {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 2px;
}
.nav-menu a { white-space: nowrap; }

.caret {
  display: inline-block;
  margin-left: 6px;
  border-top: 5px solid currentColor;   /* currentColor: the caret follows the
                                           toggle's text colour through every
                                           state with no extra rules */
  border-right: 4px solid transparent;
  border-left: 4px solid transparent;
  vertical-align: middle;
}

/* The booking link is the one action in the menu, as opposed to navigation, so
   it is painted like .button rather than like a nav pill. Filled at rest — not
   only on hover like the active-page pill — so it reads as the call to action
   whichever page you are on. */
.nav a.nav-cta {
  background: var(--clay);
  color: #fff;
  font-weight: 600;   /* weight only: any padding or line-height change here
                         would make it a different height from its neighbours */
}
.nav a.nav-cta:hover { background: var(--clay-dark); color: #fff; }

:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }

/* ---------- front page intro ---------- */

.intro { padding-bottom: 20px; text-align: center; }
.intro-line { margin: 0; font-size: 21px; }
.intro-sub  { margin: 6px 0 0; color: var(--muted); }

/* Still used by the booking link on the pricing page, not on the front page. */
.button {
  display: inline-block;
  padding: 11px 22px;
  border: 1px solid var(--clay);
  border-radius: 4px;
  background: var(--clay);
  color: #fff;
  font-size: 16px;
  text-decoration: none;
  transition: background-color .2s, color .2s;
}
.button:hover { background: var(--clay-dark); border-color: var(--clay-dark); color: #fff; }

/* ---------- modal ---------- */

body.modal-open { overflow: hidden; }

.modal-overlay {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 100;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;    /* becomes the even gap around the box */
  overflow: hidden;      /* the overlay itself never scrolls */
  background: rgba(30,24,18,.55);
}
.modal-overlay[hidden] { display: none; }   /* required: display:flex beats [hidden] */

.modal-box {
  display: flex;
  position: relative;
  flex-direction: column;
  width: 100%;
  max-width: 820px;
  max-height: 100%;
  padding: 30px 35px 0;   /* bottom padding lives on .modal-body instead */
  border-radius: 6px;
  background: var(--page);
  box-shadow: 0 12px 40px rgba(0,0,0,.3);
  animation: modal-in .18s ease-out;
}

@keyframes modal-in { from { opacity: 0; transform: translateY(-12px); } }
@media (prefers-reduced-motion: reduce) { .modal-box { animation: none; } }

/* The overlay is a fixed frame and scrolling happens inside .modal-body, which
   keeps the bottom gap identical to the top and the close button fixed in
   place on long pages. */
.modal-body {
  overflow-y: auto;
  overscroll-behavior: contain;    /* the page behind never takes over the wheel */
  padding-bottom: 35px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;           /* Firefox */
  -ms-overflow-style: none;        /* old Edge/IE */
}
.modal-body::-webkit-scrollbar { display: none; }   /* WebKit/Blink */

/* Hiding the scrollbar removes the only affordance saying content continues,
   so the box replaces it with a bottom fade, toggled by script and shown only
   while there genuinely is more to scroll. */
.modal-box::after {
  content: "";
  position: absolute;
  right: 2px; bottom: 2px; left: 2px;
  height: 55px;
  border-radius: 0 0 6px 6px;
  background: linear-gradient(to bottom, rgba(255,253,250,0), var(--page) 75%);
  opacity: 0;
  pointer-events: none;
  transition: opacity .15s ease-out;
}
.modal-box.has-more::after { opacity: 1; }

.modal-close {
  position: absolute;
  top: 8px; right: 16px;
  z-index: 1;
  color: #d24b3f;
  font-size: 30px;
  line-height: 1;
  text-decoration: none;
}
.modal-close:hover { color: #9e2f26; }

/* ---------- typography ---------- */

h1, h2, h3 { line-height: 1.25; margin: 1.6em 0 .5em; font-weight: 600; }
h1 { margin-top: 0; color: var(--clay); font-size: 30px; }
h2 { font-size: 23px; }
h3 { font-size: 19px; }

a       { color: var(--clay); }
a:hover { color: var(--clay-dark); }

small { color: var(--muted); font-size: 14px; }

/* The site's one data-display idiom: an uppercase muted micro-label above a
   large teal figure. Reuse it rather than inventing new ones. */
.label {
  display: block;
  color: var(--muted);
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.figure { color: var(--teal); font-size: 24px; font-weight: 700; }

.lede { font-size: 19px; color: var(--muted); }

/* ---------- tables ---------- */

/* Every table is wrapped so it scrolls inside its own box instead of breaking
   the page, and cells do not wrap by default: a timetable of times and levels
   reads better scrolling sideways than reflowing. One prose column opts in
   with .wrap. */
.table-wrap { overflow-x: auto; margin: 1.5em 0; -webkit-overflow-scrolling: touch; }

table { width: 100%; border-collapse: collapse; font-size: 15px; }

th, td {
  padding: 8px 10px;
  border: 1px solid var(--rule);
  text-align: left;
  white-space: nowrap;
}
td.wrap     { white-space: normal; }
td[rowspan] { vertical-align: top; }

thead th {
  padding-top: 12px; padding-bottom: 12px;
  background-color: var(--teal);
  color: #fff;
  font-weight: 600;
}
tbody tr:nth-child(even) { background-color: var(--stripe); }
tbody tr:hover           { background-color: #ece6dd; }

/* Colour alone would not distinguish these for a colour-blind reader, so the
   glyphs differ in shape too and every cell carries hidden text. */
td.bool { text-align: center; font-size: 17px; line-height: 1; }
td.yes  { color: var(--green); }
td.no   { color: #d24b3f; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

/* ---------- map ---------- */

/* The fallback address and the map are the same element at the same fixed
   height, so the map replacing the address costs no layout shift. */
.map {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 340px;
  margin: 1.5em 0;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--stripe);
  text-align: center;
}

.map-prompt p { margin: 0 0 12px; }
.map-address  { font-size: 17px; line-height: 1.5; }

.button-map { font-size: 15px; padding: 9px 18px; }

/* The map pin: a speech bubble carrying the navigation arrow, its tail resting
 * on the studio's coordinate. It replaces Leaflet's default marker image
 * entirely, so the pin and the "navigate" action are one object.
 *
 * Every selector is over-qualified on purpose. leaflet.css is injected into
 * <head> at runtime, so it always lands AFTER this stylesheet and wins at equal
 * specificity: `.leaflet-div-icon` paints a white box with a border, and
 * `.leaflet-container a` repaints links default blue.
 */
.leaflet-div-icon.map-pin-icon {
  border: 0;
  background: none;
}

.leaflet-container a.map-pin,
.map-pin {
  display: flex;
  position: relative;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 14px;
  background: var(--clay);
  color: #fff;
  text-decoration: none;
  /* drop-shadow, not box-shadow: it follows the tail's silhouette instead of
     drawing a rectangle behind the whole icon. */
  filter: drop-shadow(0 2px 5px rgba(0,0,0,.35));
  transition: background-color .2s;
}

/* The bubble's tail. Pure CSS borders, and it inherits the bubble's colour
   through currentColor so hover only has to change one property. */
.map-pin::after {
  content: "";
  position: absolute;
  bottom: -8px;
  left: 50%;
  width: 0;
  height: 0;
  margin-left: -7px;
  border-top: 9px solid var(--clay);
  border-right: 7px solid transparent;
  border-left: 7px solid transparent;
  transition: border-top-color .2s;
}

.leaflet-container a.map-pin:hover { background: var(--clay-dark); color: #fff; }
.leaflet-container a.map-pin:hover::after { border-top-color: var(--clay-dark); }

/* The arrow's visual mass sits low-left of its bounding box, so centre it by
   eye rather than by geometry. */
.map-pin svg { display: block; margin: -1px 0 0 1px; }

/* ---------- weekly timetable grid ---------- */

/* A day per row, a class per cell, in the shape of the printed poster. It is a
   real table because it is real tabular data: a row header per day, so a screen
   reader announces "Wednesday" before reading its classes out. */
.timetable { table-layout: fixed; }

.timetable td,
.timetable th {
  padding: 10px 12px;
  vertical-align: middle;
  white-space: normal;   /* class titles wrap; the default no-wrap would force a scrollbar */
}

.timetable-day {
  width: 88px;
  background: var(--stripe);
  font-size: 17px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
}

.timetable-date {
  display: block;
  color: var(--muted);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}

/* The micro-label idiom: small uppercase time above the value it annotates. */
.slot-time {
  display: block;
  color: var(--teal);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .04em;
}

.slot-title { display: block; font-size: 15px; }

.timetable-closed {
  color: var(--muted);
  font-size: 15px;
  text-align: center;
}

.timetable-empty { background: var(--page); }

/* Rows are read across, not compared down, so the zebra striping and hover
   highlight the ordinary tables use would only add noise here. */
.timetable tbody tr:nth-child(even) { background: none; }
.timetable tbody tr:hover           { background: none; }

/* ---------- asides: same shape, different rule colour ---------- */

blockquote {
  margin: 1.5em 0;
  padding: 15px 20px;
  border-left: 4px solid var(--green);
  background: var(--stripe);
  font-size: 16px;
  line-height: 1.5;
  overflow-x: auto;
}

.note {
  padding: 12px 16px;
  border-left: 4px solid var(--clay);
  background: var(--stripe);
}

/* ---------- FAQ: native <details>, no JavaScript ---------- */

/* The browser supplies open/close, keyboard support and find-in-page; only the
   marker is restyled. */
.faq details { border-bottom: 1px solid var(--rule); }

.faq summary {
  padding: 14px 30px 14px 0;
  position: relative;
  font-size: 18px; font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }

.faq summary::after {
  content: "+";
  position: absolute; top: 12px; right: 4px;
  color: var(--clay); font-size: 22px; line-height: 1;
}
.faq details[open] summary::after { content: "\2013"; }   /* en dash */
.faq summary:hover { color: var(--clay); }
.faq .answer { padding: 0 0 16px; color: #4a4640; }

/* ---------- card rows (classes, teachers, price tiers) ---------- */

.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin: 1.5em 0;
  padding: 0;
  list-style: none;
}
.cards > li {
  flex: 1 1 240px;   /* three across at 820px, two at tablet, one on a phone —
                        flex-wrap handles the reflow with no media query */
  padding: 18px 20px;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--page);
}
.cards h3 { margin: 0 0 6px; font-size: 18px; }
.cards p  { margin: 0; font-size: 16px; }
.cards .figure { display: block; margin: 4px 0 8px; }

.stack { margin: 1.5em 0; }
.stack dt { margin-top: 1em; font-weight: 600; }
.stack dd { margin: 2px 0 0; color: #4a4640; }

/* ---------- footer ---------- */

.site-footer {
  margin-top: auto;   /* the sticky-footer mechanism: the body is a flex column
                         with a full-viewport minimum and this absorbs the slack */
  padding: 30px 0 40px;
  color: var(--muted);
  font-size: 14px;
  text-align: center;
}
.site-footer p { margin: 4px 0; }
.site-footer a { color: var(--muted); }

/* ---------- small screens ---------- */

@media screen and (max-width: 700px) {
  /* The desktop banner is a wide composite: a photo panel with blurred wings.
     Squeezed into a phone's width the panel shrinks to a stamp and the wings
     take over, so swap in a straight portrait crop of the same photograph and
     let it cover the box. */
  .masthead {
    aspect-ratio: 1107 / 821;    /* the phone crop's real pixel ratio — 390px wide → 289px tall */
    background-image: var(--banner-mobile);
    background-position: center;
    background-size: cover;
  }

  /* At this width the menu drops into normal flow below the artwork instead of
     overlapping it (the min-width: 701px rule that lifts it does not apply). */
  .nav { margin-bottom: 20px; }
}

@media screen and (max-width: 600px) {
  body { font-size: 16px; }
  h1 { font-size: 25px; }
  h2 { font-size: 20px; }
  .intro-line { font-size: 19px; }
  .nav { gap: 4px; }
  .nav a, .nav-group-toggle { padding: 7px 11px; font-size: 15px; }
  .modal-overlay { padding: 20px 12px; }
  .modal-box { padding: 25px 18px 0; }
}
