/* ==========================================================================
   Calypso Residence — reset, elements, typography, layout, utilities
   Depends on tokens.css.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fonts are loaded from Google Fonts in the layout <head>, with preconnect,
   display=swap, and a &text= subset trick avoided in favour of the full Latin
   range (guest names and Nigerian place names need the accents).

   The fallback stacks in --font-display / --font-sans are chosen so that if
   Google is slow or blocked — a real risk on a constrained connection — the
   page still reads as intended rather than collapsing to Times New Roman:
   Georgia carries the serif display, system-ui carries the UI.
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchor targets clear the sticky nav. */
  scroll-padding-top: calc(var(--space-24));
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg-base);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  font-weight: var(--weight-regular);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-variant-numeric: tabular-nums lining-nums;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

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

input, button, textarea, select {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

button { background: none; border: 0; padding: 0; cursor: pointer; }

table { border-collapse: collapse; border-spacing: 0; width: 100%; }

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--space-8) 0;
}

address { font-style: normal; }

/* ==========================================================================
   FOCUS — visible, gold, and never removed
   ========================================================================== */
:focus { outline: none; }

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-xs);
}

.on-dark :focus-visible,
.display-screen :focus-visible {
  box-shadow: var(--focus-ring-dark);
}

/* Skip link — first tab stop on every page. */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: var(--space-4);
  z-index: var(--z-toast);
  padding: var(--space-3) var(--space-5);
  background: var(--bg-deep);
  color: var(--text-on-dark);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform var(--dur-fast) var(--ease-state);
}
.skip-link:focus-visible { transform: translateY(0); }

/* ==========================================================================
   TYPOGRAPHY
   ========================================================================== */
.display-1, .display-2, .display-3, .display-4 {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  font-optical-sizing: auto;
  text-wrap: balance;
}
.display-1 { font-size: var(--text-hero); }
.display-2 { font-size: var(--text-4xl); }
.display-3 { font-size: var(--text-3xl); }
.display-4 { font-size: var(--text-2xl); }

/* Serif headings by default inside prose and section headers. */
.section-title {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
  margin: 0;
}
.section-title--sm { font-size: var(--text-2xl); }

/* Eyebrow: small caps, wide tracking, gold. Uses --gold-ink for AA. */
.eyebrow {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--gold-ink);
  margin-bottom: var(--space-3);
}
.on-dark .eyebrow,
.display-screen .eyebrow { color: var(--gold-soft); }

.section-lede {
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
  color: var(--text-muted);
  max-width: var(--container-text);
  text-wrap: pretty;
}

.section-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-12);
}
.section-head--center {
  align-items: center;
  text-align: center;
}
.section-head--center .section-lede { margin-inline: auto; }

.text-muted  { color: var(--text-muted); }
.text-faint  { color: var(--text-faint); }
.text-gold   { color: var(--gold-ink); }        /* AA-safe on light */
.on-dark .text-gold { color: var(--gold-soft); }
.text-success{ color: var(--success-ink); }
.text-warning{ color: var(--warning-ink); }
.text-danger { color: var(--danger-ink); }
.text-info   { color: var(--info-ink); }

.text-xs   { font-size: var(--text-xs); }
.text-sm   { font-size: var(--text-sm); }
.text-lg   { font-size: var(--text-lg); }
.font-medium   { font-weight: var(--weight-medium); }
.font-semibold { font-weight: var(--weight-semibold); }
.font-display  { font-family: var(--font-display); }
.font-mono     { font-family: var(--font-mono); }

/* Money is tabular so columns of naira line up. */
.money {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
  white-space: nowrap;
}
.money--negative { color: var(--danger-ink); }

/* Long-form copy */
.prose { max-width: var(--container-text); }
.prose > * + * { margin-top: var(--space-5); }
.prose p { line-height: var(--leading-relaxed); color: var(--text-secondary); }
.prose h2 { font-family: var(--font-display); font-size: var(--text-2xl); margin-top: var(--space-10); }
.prose h3 { font-family: var(--font-display); font-size: var(--text-xl); margin-top: var(--space-8); }
.prose a { color: var(--gold-ink); text-underline-offset: 3px; }
.prose ul { list-style: disc; padding-left: var(--space-6); }
.prose li + li { margin-top: var(--space-2); }

a { color: var(--gold-ink); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--gold-dark); }
.on-dark a { color: var(--gold-soft); }

/* ==========================================================================
   LAYOUT
   ========================================================================== */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--wide { max-width: var(--container-wide); }
.container--text { max-width: var(--container-text); }
.container--flush { padding-inline: 0; }

/* Section bands. The public site alternates base / sand / deep. */
.band { padding-block: var(--band-y); position: relative; }
.band--sand { background: var(--bg-sand); }
.band--deep {
  background: var(--bg-deep);
  color: var(--text-on-dark);
}
.band--deep-alt { background: var(--bg-deep-alt); color: var(--text-on-dark); }
.band--tight { padding-block: var(--space-12); }

/* Everything inside .on-dark flips ink automatically. */
.on-dark,
.band--deep,
.band--deep-alt {
  --text-primary: var(--text-on-dark);
  --text-secondary: var(--text-on-dark-muted);
  --text-muted: var(--text-on-dark-muted);
  --line: var(--line-on-dark);
  --bg-elevated: var(--bg-deep-alt);
  --gold-ink: var(--gold-soft);
}

.stack { display: flex; flex-direction: column; }
.stack > * + * { margin-top: var(--gap, var(--space-4)); }

.row {
  display: flex;
  align-items: center;
  gap: var(--gap, var(--space-3));
}
.row--between { justify-content: space-between; }
.row--end     { justify-content: flex-end; }
.row--center  { justify-content: center; }
.row--top     { align-items: flex-start; }
.row--wrap    { flex-wrap: wrap; }

.grid { display: grid; gap: var(--gap, var(--space-6)); }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* Auto-fitting card grid — the rooms listing and facilities grid use this. */
.grid--auto {
  grid-template-columns: repeat(auto-fill, minmax(var(--min-col, 280px), 1fr));
}

@media (max-width: 1024px) {
  .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .grid--2, .grid--3, .grid--4 { grid-template-columns: minmax(0, 1fr); }
}

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(var(--space-8), 5vw, var(--space-20));
  align-items: center;
}
@media (max-width: 900px) { .split { grid-template-columns: 1fr; } }

/* Sticky sidebar — the room-detail booking card. */
.sticky-panel {
  position: sticky;
  top: calc(var(--space-24) + var(--space-2));
}
@media (max-width: 900px) { .sticky-panel { position: static; } }

/* ==========================================================================
   UTILITIES
   ========================================================================== */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.hidden { display: none !important; }
@media (max-width: 768px)  { .hide-mobile  { display: none !important; } }
@media (min-width: 769px)  { .only-mobile  { display: none !important; } }
@media (max-width: 1024px) { .hide-tablet  { display: none !important; } }

.text-center { text-align: center; }
.text-right  { text-align: right; }
.full-width  { width: 100%; }
.nowrap      { white-space: nowrap; }
.truncate    { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.clamp-2, .clamp-3 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }
.clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }

.mt-0 { margin-top: 0; }   .mt-2 { margin-top: var(--space-2); }
.mt-4 { margin-top: var(--space-4); } .mt-6 { margin-top: var(--space-6); }
.mt-8 { margin-top: var(--space-8); } .mt-12 { margin-top: var(--space-12); }
.mb-0 { margin-bottom: 0; } .mb-2 { margin-bottom: var(--space-2); }
.mb-4 { margin-bottom: var(--space-4); } .mb-6 { margin-bottom: var(--space-6); }
.mb-8 { margin-bottom: var(--space-8); }

/* Hairline gold rule — used above and below the assurances band. */
.rule-gold {
  height: 1px;
  background: linear-gradient(
    to right,
    transparent,
    var(--gold) 15%,
    var(--gold) 85%,
    transparent
  );
  opacity: 0.5;
  border: 0;
  margin: 0;
}

/* Low-opacity warm glow behind a band's centre. */
.glow-gold::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 60% 70% at 50% 50%,
    var(--gold-wash) 0%,
    transparent 70%
  );
  pointer-events: none;
}

/* Film grain overlay for the hero. */
.grain::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ==========================================================================
   MOTION PRIMITIVES
   Used by animations.js via IntersectionObserver, and degraded to plain
   opacity under prefers-reduced-motion by the token overrides.
   ========================================================================== */

/* Page entry: soft fade + 12px rise as the page paints (§9). */
@keyframes page-enter {
  from { opacity: 0; transform: translate3d(0, 12px, 0); }
  to   { opacity: 1; transform: none; }
}
main {
  /* End state declared, and NO fill-mode — the same lesson as .slideover and
     [data-reveal]. `both` pins the element to the FROM frame whenever the
     animation does not actually run, which here means the whole page sits
     12px lower than its own box: every screen gains a stray scrollbar and
     anything designed to fit exactly (the till) is pushed under the fold.
     Losing a 12px rise is nothing; displacing the entire page is not. */
  opacity: 1;
  transform: none;
  animation: page-enter var(--dur-slow) var(--ease-entrance);
}
@media (prefers-reduced-motion: reduce) {
  main { animation: none; }
}

/* Scroll reveal: children fade up 24px with a stagger.
   ---------------------------------------------------------------------------
   A KEYFRAME ANIMATION, not a transition — and that is the whole point.

   This used to be `transition: opacity …, transform …`. It never ran. Several
   components set their own `transition:` for hover work — .card,
   .assurances__item, .gallery-item — and `transition` is a SHORTHAND, so
   `transition: transform var(--dur-base)` does not add to the reveal, it
   replaces it and drops opacity entirely. Those rules sit in public.css with
   the same specificity as [data-reveal] and load afterwards, so they won.
   The result: elements snapped from invisible to visible with no fade, which
   reads as "the animations don't work" because they genuinely did not.

   An animation cannot be clobbered that way. A component is free to transition
   whatever it likes on hover and the reveal still plays. */
[data-reveal] { opacity: 0; }

[data-reveal].is-revealed {
  /* The end state is declared OUTRIGHT, not left to the animation.
     `animation-fill-mode: both` would otherwise hold the element on its FROM
     frame — opacity 0 — in any situation where the animation never actually
     runs: a frozen background tab, a browser that skips animations, a device
     under memory pressure. Declaring opacity here means the worst case is a
     missing flourish rather than missing text. */
  opacity: 1;
  /* 'backwards', not 'both': the FROM frame is needed during the stagger
     delay so nothing flashes in early, but once the animation is over the
     element must fall back to the opacity:1 declared above rather than being
     pinned to the animation's last frame. */
  animation: reveal-up var(--dur-slow) var(--ease-entrance) backwards;
  animation-delay: var(--reveal-delay, 0ms);
}

@keyframes reveal-up {
  from { opacity: 0; transform: translate3d(0, 24px, 0); }
  to   { opacity: 1; transform: none; }
}

/* No JS? Nothing stays invisible. */
.no-js [data-reveal] { opacity: 1; animation: none; }

@media (prefers-reduced-motion: reduce) {
  /* Still appear — just without travelling. */
  [data-reveal].is-revealed { animation: reveal-fade var(--dur-quick) var(--ease-entrance) backwards; }
  @keyframes reveal-fade { from { opacity: 0; } to { opacity: 1; } }
}

/* SVG line icons that draw themselves in. */
.draw-in path,
.draw-in circle,
.draw-in line,
.draw-in polyline {
  stroke-dasharray: var(--draw-length, 120);
  stroke-dashoffset: var(--draw-length, 120);
  transition: stroke-dashoffset var(--dur-slower) var(--ease-entrance);
  transition-delay: var(--reveal-delay, 0ms);
}
.draw-in.is-revealed path,
.draw-in.is-revealed circle,
.draw-in.is-revealed line,
.draw-in.is-revealed polyline {
  stroke-dashoffset: 0;
}
.no-js .draw-in path,
.no-js .draw-in circle,
.no-js .draw-in line,
.no-js .draw-in polyline { stroke-dashoffset: 0; }

@media (prefers-reduced-motion: reduce) {
  .draw-in path, .draw-in circle, .draw-in line, .draw-in polyline {
    stroke-dashoffset: 0;
    transition: none;
  }
}

/* Shimmer, shared by every skeleton. */
@keyframes shimmer {
  from { background-position: -200% 0; }
  to   { background-position: 200% 0; }
}

/* Gentle infinite bounce for the hero scroll cue. */
@keyframes scroll-cue {
  0%, 100% { transform: translateY(0); opacity: 0.7; }
  50%      { transform: translateY(8px); opacity: 1; }
}

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

@keyframes scale-fade-in {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes slide-in-right {
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
}

@keyframes spin { to { transform: rotate(360deg); } }

/* Pulse ring — a room tile that just changed status. */
@keyframes pulse-ring {
  0%   { box-shadow: 0 0 0 0 var(--pulse-color, var(--gold)); }
  70%  { box-shadow: 0 0 0 10px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* ==========================================================================
   PRINT — folios, orders, reports (§4.5, §5.6)
   ========================================================================== */
@media print {
  :root { --bg-base: #fff; --bg-sand: #fff; }
  body { background: #fff; color: #000; font-size: 11pt; }

  .site-nav, .site-footer, .console-sidebar, .console-topbar,
  .no-print, .toast-stack, .skip-link, .loading-bar { display: none !important; }

  main { animation: none; }
  [data-reveal] { opacity: 1 !important; transform: none !important; }

  .card, .panel { box-shadow: none; border: 1px solid #ccc; break-inside: avoid; }
  a[href]::after { content: ''; }
  thead { display: table-header-group; }
  tr, img { break-inside: avoid; }
}

/* ===========================================================================
   SPACING AND TEXT UTILITIES  —  the complete set
   ---------------------------------------------------------------------------
   Only mt-0/4/8 and mb-0/4/8 existed. The templates use mt-1, mt-2, mt-3,
   mt-5, mt-6, mb-1 … mb-6 throughout, and every one of those was doing
   nothing — which is why blocks all over the console sat flush against each
   other with no breathing room.

   Generated straight from the space tokens so the scale stays the one scale.
   =========================================================================== */

.mt-0 { margin-top: 0 !important; }
.mt-1 { margin-top: var(--space-1) !important; }
.mt-2 { margin-top: var(--space-2) !important; }
.mt-3 { margin-top: var(--space-3) !important; }
.mt-4 { margin-top: var(--space-4) !important; }
.mt-5 { margin-top: var(--space-5) !important; }
.mt-6 { margin-top: var(--space-6) !important; }
.mt-8 { margin-top: var(--space-8) !important; }
.mt-10 { margin-top: var(--space-10) !important; }
.mt-12 { margin-top: var(--space-12) !important; }

.mb-0 { margin-bottom: 0 !important; }
.mb-1 { margin-bottom: var(--space-1) !important; }
.mb-2 { margin-bottom: var(--space-2) !important; }
.mb-3 { margin-bottom: var(--space-3) !important; }
.mb-4 { margin-bottom: var(--space-4) !important; }
.mb-5 { margin-bottom: var(--space-5) !important; }
.mb-6 { margin-bottom: var(--space-6) !important; }
.mb-8 { margin-bottom: var(--space-8) !important; }
.mb-10 { margin-bottom: var(--space-10) !important; }
.mb-12 { margin-bottom: var(--space-12) !important; }

.p-0 { padding: 0 !important; }
.p-3 { padding: var(--space-3) !important; }
.p-4 { padding: var(--space-4) !important; }
.p-6 { padding: var(--space-6) !important; }

/* Numbers in a table: right-aligned and tabular, so columns of naira line up
   digit under digit instead of drifting. */
.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Force a normally-inline element onto its own line. Used for the second line
   of a table cell — a timestamp under a name. */
.block { display: block; }

.w-full { width: 100%; }
.text-nowrap { white-space: nowrap; }
