/* ============================================================================
   base.css — reset + Thai typography floor, shared by the gallery and all
   twelve showcase sites. Carries no colour, no font family and no personality:
   every one of those belongs to the per-site stylesheet loaded after this one.
   ============================================================================ */

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

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

body {
  margin: 0;
  /* Thai has stacked vowels and tone marks above and below the baseline; anything under about
     1.75 makes consecutive lines collide. Sites may raise this, never lower it for body copy. */
  line-height: 1.8;
  font-size: 1.0625rem;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

h1, h2, h3, h4 {
  line-height: 1.3;
  font-weight: 600;
  text-wrap: balance;
}

p { text-wrap: pretty; }

/* Thai text must never be italicised or letter-spaced apart: both detach the vowel and tone
   marks from their consonant. Enforced here so no site can reintroduce it by accident. */
:lang(th) i, :lang(th) em, i, em { font-style: normal; }
body { letter-spacing: 0; }

/* Thai has no inter-word spaces, so a long unbroken run must be allowed to wrap anywhere the
   renderer decides — but never mid-word in Latin text sitting in the same paragraph. */
p, li, dd, figcaption { word-break: normal; overflow-wrap: anywhere; }

img, picture, svg, video { display: block; max-width: 100%; height: auto; }

a { color: inherit; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

table { border-collapse: collapse; width: 100%; }
th { text-align: left; font-weight: 600; }

/* Every site overrides the colour; the shape of the focus ring stays constant across all
   thirteen so keyboard users get one consistent signal. */
:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
:focus:not(:focus-visible) { outline: none; }

/* Financial figures, prices, times and measurements are set in a mono face on every site and
   must line up in columns wherever they stack. */
.num { font-variant-numeric: tabular-nums; }

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

.skip-link {
  position: absolute;
  left: 0; top: -100%;
  z-index: 100;
  padding: 12px 20px;
  background: #000;
  color: #fff;
  text-decoration: none;
}
.skip-link:focus { top: 0; }

/* The course credit in every footer. Several sites set `.footer a { text-decoration: none }`,
   and their sheet loads after this one — so the selector has to out-rank that, not just match it. */
a.credit, .footer a.credit, .footer__note a.credit {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Close control for the mobile nav overlay.

   The overlay is position:fixed;inset:0 on every site, which covers the burger that opened it —
   and on the sites whose header is not sticky the burger has scrolled away entirely, so there
   was no way back out except the Escape key, which a phone does not have. base.js injects this
   button into the panel rather than adding it to twelve layouts; its colour is copied from the
   panel's own links at runtime so it reads against whatever that site's overlay is. */
.nav-close { display: none; }
.nav.is-open .nav-close {
  display: block;
  position: absolute; top: 10px; right: 10px;
  width: 44px; height: 44px; padding: 0;
  background: none; border: 0; cursor: pointer;
}
.nav-close::before, .nav-close::after {
  content: '';
  position: absolute; left: 11px; top: 21px;
  width: 22px; height: 2px;
  background: currentColor;
}
.nav-close::before { transform: rotate(45deg); }
.nav-close::after { transform: rotate(-45deg); }

/* Wide content — comparison tables, timetables, dispatch boards — scrolls inside its own box.
   The page body itself must never scroll sideways. */
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* Reveal-on-scroll, driven by base.js. Elements start visible and are only hidden once JS has
   confirmed it can un-hide them, so the page is fully readable with JS off or broken. */
.js-reveal.is-armed { opacity: 0; transform: translateY(16px); }
.js-reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity .5s ease-out, transform .5s ease-out;
}

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