/* =========================================================================
   Ocean Breeze Spa — main stylesheet
   Brand concept "C": Kinfolk (display serif) / Brittany Signature (script)
   / Roboto (body). Coastal blue palette sampled from the approved brand
   board.
   ========================================================================= */

:root {
	/* ---- Brand palette (Option C, sampled from brand board) ---- */
	--obs-blue:        #7695C1; /* primary — decorative use, large text only */
	--obs-blue-deep:   #496EA2; /* same hue/saturation, darkened so white text
	                              clears WCAG AA (5.2:1). Use for any solid
	                              button or fill that carries white text. */
	--obs-blue-light:  #BCCCDB; /* secondary */
	--obs-mauve:       #90827F; /* accent */
	--obs-warm-gray:   #BFBAB4;
	--obs-cream:       #E2D9D4;

	--obs-navy:        #16283C; /* derived deep ink for text/footer, harmonizes with --obs-blue */
	--obs-navy-2:      #1F3550;
	--obs-white:       #FFFFFF;
	--obs-off-white:   #FAF8F6;
	--obs-ink:         #22303F;
	--obs-ink-soft:    #4C5B6B;
	--obs-line:        #E4E1DC;

	/* ---- Type ---- */
	--obs-font-display: 'Kinfolk', Georgia, 'Times New Roman', serif;
	--obs-font-script:  'Brittany Signature', cursive;
	--obs-font-body:    'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

	/* ---- Layout ---- */
	--obs-container: 1200px;
	--obs-radius: 6px;
	--obs-radius-lg: 14px;
	--obs-shadow: 0 10px 30px -12px rgba(22, 40, 60, 0.25);
	--obs-shadow-soft: 0 4px 16px rgba(22, 40, 60, 0.08);
	--obs-header-h: 96px;
}

/* ---------------------------------------------------------------------
   Reset / base
--------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
	margin: 0;
	font-family: var(--obs-font-body);
	color: var(--obs-ink);
	background: var(--obs-white);
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
	overflow-x: hidden;
}
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, h4 { margin: 0; font-weight: 400; }
p { margin: 0 0 1em; }
button { font-family: inherit; cursor: pointer; }
input, textarea, select { font-family: inherit; font-size: 1rem; }

.screen-reader-text {
	position: absolute; width: 1px; height: 1px; overflow: hidden;
	clip: rect(1px, 1px, 1px, 1px); white-space: nowrap;
}
.obs-skip-link {
	position: absolute; left: -999px; top: 0; z-index: 10000;
	background: var(--obs-navy); color: #fff; padding: 12px 20px;
}
.obs-skip-link:focus { left: 10px; top: 10px; }

.obs-container {
	max-width: var(--obs-container);
	margin: 0 auto;
	padding: 0 28px;
}

.obs-eyebrow {
	font-family: var(--obs-font-script);
	font-weight: 400;
	letter-spacing: 0.01em;
	text-transform: none;
	/* The script face has a small x-height for its point size, so it reads
	   noticeably smaller than the number suggests — it needs to be set larger
	   than a sans would be to carry the same visual weight. Fluid so the
	   longest eyebrows ("Why Guests Choose Us", "Visit or Reach Out") stay
	   manageable on narrow screens. */
	font-size: clamp(2rem, 3.7vw, 3.4rem);
	/* If a long eyebrow does wrap on a narrow screen, split it evenly rather
	   than orphaning a single short word on the second line. */
	text-wrap: balance;
	/* Script face with long descenders — line-height 1 clipped them against
	   the heading below, and 6px was not enough separation. */
	line-height: 1.15;
	color: var(--obs-blue);
	margin: 0 0 20px;
}

.obs-h2 {
	font-family: var(--obs-font-display);
	font-size: clamp(2rem, 3.4vw, 2.9rem);
	/* These headings routinely run to two lines and mix the display serif
	   with a larger script span, so 1.15 read as cramped. */
	line-height: 1.32;
	color: var(--obs-navy);
	margin-bottom: 0.7em;
}
.obs-script {
	font-family: var(--obs-font-script);
	font-weight: 400;
	color: var(--obs-blue);
	font-size: 1.15em;
	display: inline-block;
	/* Set larger than its parent and full of ascenders/descenders, so it
	   needs its own leading rather than inheriting the heading's. */
	line-height: 1.25;
}

.obs-section-head { max-width: 680px; margin: 0 auto 48px; text-align: center; }
.obs-section-head__sub { color: var(--obs-ink-soft); font-size: 1.05rem; }

.obs-section { padding: 96px 0; }
.obs-section--tint { background: var(--obs-off-white); }

.obs-prose p { color: var(--obs-ink-soft); }
.obs-prose--tight p { margin-bottom: 0.6em; font-size: 0.95rem; }

.obs-empty-state {
	text-align: center;
	padding: 48px;
	border: 1px dashed var(--obs-line);
	border-radius: var(--obs-radius-lg);
	color: var(--obs-ink-soft);
	background: var(--obs-white);
}

/* Placeholder blocks for photos not yet uploaded — visible only as a
   subtle dev aid; swap in real images via the featured image field and
   these disappear automatically. */
.obs-image-placeholder {
	background: linear-gradient(135deg, var(--obs-blue-light) 0%, var(--obs-cream) 100%);
	display: flex;
	align-items: center;
	justify-content: center;
	position: relative;
	min-height: 220px;
}
.obs-image-placeholder::after {
	content: attr(data-placeholder-label);
	font-size: 0.75rem;
	color: rgba(22, 40, 60, 0.55);
	background: rgba(255,255,255,0.65);
	padding: 6px 12px;
	border-radius: 100px;
	text-align: center;
}

/* ---------------------------------------------------------------------
   Buttons
--------------------------------------------------------------------- */
.obs-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	font-family: var(--obs-font-body);
	font-weight: 600;
	font-size: 0.9rem;
	letter-spacing: 0.03em;
	padding: 14px 28px;
	border-radius: var(--obs-radius);
	border: 1px solid transparent;
	transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease, color 0.15s ease;
	white-space: nowrap;
}
.obs-btn svg { width: 16px; height: 16px; }
.obs-btn:hover { transform: translateY(-2px); }

.obs-btn--primary { background: var(--obs-blue-deep); color: #fff; box-shadow: var(--obs-shadow-soft); }
.obs-btn--primary:hover { background: var(--obs-navy-2); }

.obs-btn--outline { background: transparent; border-color: currentColor; color: var(--obs-navy); }
.obs-btn--outline:hover { background: var(--obs-navy); border-color: var(--obs-navy); color: #fff; }

.obs-btn--pill { background: var(--obs-cream); color: var(--obs-navy); border-radius: 100px; padding: 15px 34px; }
.obs-btn--pill:hover { background: #fff; }

.obs-btn--book { background: var(--obs-navy); color: #fff; width: 100%; margin-top: 14px; font-size: 0.85rem; padding: 12px 20px; }
.obs-btn--book:hover { background: var(--obs-blue-deep); }

/* ---------------------------------------------------------------------
   Header
--------------------------------------------------------------------- */
.obs-header {
	position: fixed;
	top: 0; left: 0; right: 0;
	z-index: 500;
	height: var(--obs-header-h);
	/* Matched to the logo crossfade (0.5s, same curve). At 0.3s the bar
	   finished resizing while the crest was still mid-fade, so the logo
	   appeared to detach from the bar as it moved. */
	transition:
		background-color 0.5s cubic-bezier(0.22, 0.61, 0.36, 1),
		box-shadow 0.5s cubic-bezier(0.22, 0.61, 0.36, 1),
		height 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.obs-header__inner {
	max-width: var(--obs-container);
	margin: 0 auto;
	height: 100%;
	padding: 0 28px;
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: 24px;
}
.obs-header__nav { display: flex; }
.obs-header__nav--right { justify-content: flex-end; align-items: center; gap: 20px; }
.obs-nav-list { display: flex; gap: 28px; }
.obs-nav-list a {
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: #fff;
	opacity: 0.92;
	transition: opacity 0.2s ease;
}
.obs-nav-list a:hover { opacity: 1; }

.obs-header__logo-link { display: block; line-height: 0; }

/* Explicit height matching the header bar itself (96px / 76px scrolled),
   rather than relying on the grid row's auto-sizing — which collapses to
   fit only the smallest visible content once the logo images inside are
   absolutely positioned (see below) and stop contributing to row sizing.
   This gives `top:50%` on the absolutely-positioned logo images something
   reliable to center against, and guarantees the row itself is always
   exactly as tall as the visible bar (so nav links never end up centered
   in some much-shorter implicit row and drift outside the bar). */
.obs-header__logo {
	position: relative;
	height: var(--obs-header-h);
	display: flex;
	align-items: center;
	justify-content: center;
}
.obs-header.is-scrolled .obs-header__logo { height: 76px; }

/* Compact wordmark — base rules apply on every page. On inner pages (no
   hero/full-logo moment) this is simply always visible, vertically centered
   in the bar via the flex centering on .obs-header__logo above. */
.obs-header__logo .obs-logo {
	height: 56px;
	width: auto;
	max-width: none;
	/* max-height is included because the compact wordmark is sized by its
	   cap on small screens; without it the logo jumped size in one frame
	   while its opacity was still fading. */
	transition:
		opacity 0.5s cubic-bezier(0.22, 0.61, 0.36, 1),
		height 0.5s cubic-bezier(0.22, 0.61, 0.36, 1),
		max-height 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
	display: block;
}
/* Two-line lockup (wordmark + flourish + tagline) at ~4.2:1, so it needs
   more height than the single-line wordmark it replaced or the tagline
   becomes unreadable. Sized to sit comfortably inside the 76px bar. */
.obs-header.is-scrolled .obs-header__logo .obs-logo { height: 52px; }
.obs-header__logo .obs-logo--navy { display: none; }

/* Full illustrated logo — hidden by default (inner pages never show it). */
.obs-header__logo .obs-logo-full { display: none; }

/* =========================================================================
   Homepage-only: animated crossfade between the full logo and the compact
   wordmark. Both images are absolutely positioned against `.obs-header__logo`
   (not the header's grid row), so their real height — 176px, taller than
   the header bar itself — never inflates the row that the nav links sit
   in. Without this, the row would grow to fit the tall logo and push the
   nav links down below the visible bar.
   ========================================================================= */
body.home .obs-header__logo .obs-logo-full {
	display: block;
	position: absolute;
	top: 26px; /* <- the requested breathing room from the top of the screen */
	left: 50%;
	height: 176px;
	width: auto;
	max-width: none;
	transform: translateX(-50%) scale(1);
	transform-origin: top center;
	opacity: 1;
	transition: opacity 0.5s cubic-bezier(0.22, 0.61, 0.36, 1), transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
}

body.home .obs-header__logo .obs-logo {
	position: absolute;
	top: 50%;
	left: 50%;
	height: 34px;
	width: auto;
	max-width: none;
	transform: translate(-50%, -50%) scale(0.7);
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.5s cubic-bezier(0.22, 0.61, 0.36, 1), transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Scrolled: full logo shrinks + fades out, compact wordmark grows + fades in.
   The compact wordmark is centered on .obs-header__logo itself (which is
   align-self:center within the now-correctly-sized row), so it lands
   perfectly centered in the solid bar regardless of the 96px/76px height. */
body.home .obs-header.is-scrolled .obs-header__logo .obs-logo-full {
	opacity: 0;
	transform: translateX(-50%) scale(0.4);
}
body.home .obs-header.is-scrolled .obs-header__logo .obs-logo {
	opacity: 1;
	transform: translate(-50%, -50%) scale(1);
	pointer-events: auto;
}

.obs-header__cta { display: none; }

.obs-header__toggle {
	display: none;
	background: none; border: 0; width: 34px; height: 26px;
	flex-direction: column; justify-content: space-between;
	padding: 0;
}
.obs-header__toggle span { display: block; height: 2px; background: #fff; border-radius: 2px; }

.obs-mobile-nav {
	display: none;
	position: absolute;
	top: 100%; left: 0; right: 0;
	background: var(--obs-navy);
	padding: 20px 28px 32px;
	flex-direction: column;
	gap: 4px;
}
.obs-mobile-nav-list { display: flex; flex-direction: column; }
.obs-mobile-nav-list a {
	color: #fff; padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,0.12);
	font-size: 0.95rem; letter-spacing: 0.04em; text-transform: uppercase; font-weight: 600;
	display: block;
}
.obs-mobile-nav__phone { color: var(--obs-blue-light); margin-top: 16px; display: inline-block; font-weight: 700; }

/* Header states: transparent-over-video at top, solid once scrolled */
.obs-header { background: transparent; box-shadow: none; }
.obs-header.is-scrolled {
	background: rgba(22, 40, 60, 0.96);
	box-shadow: 0 6px 24px rgba(0,0,0,0.15);
	height: 76px;
}
.obs-header.nav-open .obs-mobile-nav { display: flex; }

/* On non-front pages (no hero video behind it) keep header solid always */
body:not(.home) .obs-header { background: var(--obs-navy); }

/* Menu open: the bar must go solid even at the top of the homepage, where it
   is otherwise transparent over the hero video. Without this the open panel
   hangs off an invisible header and the links read against the video.
   Declared after the body:not(.home) rule so it wins on every page. */
.obs-header.nav-open {
	background: rgba(22, 40, 60, 0.98);
	box-shadow: none;
}

/* With the menu open, swap the tall illustrated crest for the compact
   wordmark: the crest is 116px tall and overflows the bar by design, which
   means it spills across the top of the open panel and gets clipped by it. */
body.home .obs-header.nav-open .obs-header__logo .obs-logo-full {
	opacity: 0;
	pointer-events: none;
}
body.home .obs-header.nav-open .obs-header__logo .obs-logo {
	opacity: 1;
	transform: translate(-50%, -50%) scale(1);
}

@media (max-width: 860px) {
	body.home .obs-header__logo .obs-logo-full { top: 20px; }
}
@media (max-width: 600px) {
	body.home .obs-header__logo .obs-logo-full { top: 16px; }
}


/* ---------------------------------------------------------------------
   Hero
--------------------------------------------------------------------- */
.obs-hero {
	position: relative;
	height: 100vh;
	min-height: 620px;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	color: #fff;
}
.obs-hero__media { position: absolute; inset: 0; z-index: 0; }
.obs-hero__video {
	width: 100%; height: 100%;
	object-fit: cover;
	object-position: center;
}
.obs-hero__scrim {
	position: absolute; inset: 0;
	background: linear-gradient(180deg, rgba(22,40,60,0.55) 0%, rgba(22,40,60,0.25) 40%, rgba(22,40,60,0.65) 100%);
}
.obs-hero__content {
	position: relative;
	z-index: 1;
	text-align: center;
	/* Wide enough for the script headline to hold one line on desktop. At
	   760px the longer wording wrapped and left the final word stranded
	   alone on a second line. */
	max-width: 1080px;
	padding: 0 24px;
}
.obs-hero__eyebrow {
	font-size: 0.85rem;
	letter-spacing: 0.28em;
	text-transform: uppercase;
	font-weight: 600;
	margin-bottom: 18px;
	opacity: 0.95;
}
.obs-hero__script {
	font-family: var(--obs-font-script);
	font-weight: 400;
	font-size: clamp(3rem, 8vw, 6rem);
	/*
	 * The script face has tall ascenders and deep descenders, so line-height:1
	 * made wrapped lines collide — on a phone "Devoted to / Your Calm." sat on
	 * top of itself. Desktop keeps it on one line so the extra leading is not
	 * visible there; the hero copy band is measured from the rendered box, so
	 * it re-centres itself automatically.
	 */
	line-height: 1.4;
	margin-bottom: 34px;
	text-shadow: 0 6px 30px rgba(0,0,0,0.25);
	/* Where it must wrap (tablet/phone), split the line evenly rather than
	   pushing a single short word onto its own line. Ignored by browsers
	   that don't support it, which simply wrap as before. */
	text-wrap: balance;
}
/* Hero CTA: white outline on transparent, sitting over the video. Declared
   after .obs-btn--pill so it overrides that variant's cream fill and navy
   text while keeping its pill radius and padding. */
.obs-hero__cta {
	font-size: 0.85rem;
	margin-top: 26px;
	background: transparent;
	border: 1px solid #fff;
	color: #fff;
	text-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}
.obs-hero__cta:hover {
	background: #fff;
	border-color: #fff;
	color: var(--obs-navy);
	text-shadow: none;
}

/* The cue is now a label stacked above the mouse outline, so the anchor is a
   column and the outline moved onto its own element — previously the border
   was drawn on the anchor itself, which would have boxed the label inside it. */
.obs-hero__scroll-cue {
	position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%);
	display: flex; flex-direction: column; align-items: center; gap: 10px;
	text-decoration: none;
	z-index: 1;
}
.obs-hero__scroll-cue .obs-hero__scroll-label {
	font-family: var(--obs-font-body);
	font-size: 0.68rem;
	letter-spacing: 0.22em;
	text-transform: uppercase;
	color: rgba(255,255,255,0.78);
	white-space: nowrap;
	/* Belt and braces against the legacy dot rule. */
	position: static;
	width: auto;
	height: auto;
	background: none;
	animation: none;
}
.obs-hero__scroll-cue .obs-hero__scroll-mouse {
	position: relative; display: block;
	width: 26px; height: 42px;
	border: 2px solid rgba(255,255,255,0.7); border-radius: 100px;
}
.obs-hero__scroll-cue .obs-hero__scroll-dot {
	position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
	width: 4px; height: 8px; background: #fff; border-radius: 4px;
	animation: obs-scroll-cue 1.8s ease infinite;
}
@keyframes obs-scroll-cue {
	0% { opacity: 1; top: 8px; }
	70% { opacity: 0; top: 20px; }
	100% { opacity: 0; top: 8px; }
}

/* ---------------------------------------------------------------------
   About
--------------------------------------------------------------------- */
.obs-about__grid {
	display: grid;
	/* The media column was narrower than the copy because the frame used to be
	   portrait and already stood tall. A 4:3 frame is much shorter, so it needs
	   the extra width to hold its own beside the text. */
	grid-template-columns: 1.05fr 1fr;
	gap: 64px;
	align-items: center;
}
.obs-about__media { position: relative; }
.obs-about__image {
	border-radius: var(--obs-radius-lg);
	width: 100%;
	/*
	 * Landscape 4:3. The rotation photos are all landscape (1.22–1.54), and
	 * the previous 4:5 portrait frame cut 34–39% off the width of the lobby
	 * and treatment-room renderings. 4:3 costs those renderings 1–9% instead;
	 * 3:2 would be kinder to the stock photography but takes 12–19% off the
	 * renderings, which are the shots that need their full width to read.
	 */
	aspect-ratio: 4 / 3;
	object-fit: cover;
	box-shadow: var(--obs-shadow);
}
.obs-about__badge {
	position: absolute;
	bottom: -22px; left: -22px;
	background: var(--obs-navy);
	color: #fff;
	padding: 16px 22px;
	border-radius: var(--obs-radius);
	display: flex; align-items: center; gap: 10px;
	font-size: 0.85rem; font-weight: 600; letter-spacing: 0.04em;
	box-shadow: var(--obs-shadow);
}
.obs-about__badge svg { width: 26px; height: 14px; color: var(--obs-blue-light); }

.obs-about__stats {
	display: flex;
	gap: 36px;
	margin: 28px 0 32px;
	padding-top: 24px;
	border-top: 1px solid var(--obs-line);
}
.obs-about__stats li { display: flex; flex-direction: column; }
.obs-about__stats strong { font-family: var(--obs-font-display); font-size: 2rem; color: var(--obs-blue); }
.obs-about__stats span { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--obs-ink-soft); }

/* ---------------------------------------------------------------------
   Services
--------------------------------------------------------------------- */
.obs-services__jump {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 10px;
	margin-bottom: 56px;
}
.obs-services__jump a {
	font-size: 0.78rem;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	padding: 10px 20px;
	border: 1px solid var(--obs-blue-light);
	border-radius: 100px;
	color: var(--obs-navy);
	transition: background-color 0.2s ease, color 0.2s ease;
}
.obs-services__jump a:hover { background: var(--obs-blue-deep); color: #fff; border-color: var(--obs-blue-deep); }

.obs-service-group { margin-bottom: 64px; scroll-margin-top: 110px; }
.obs-service-group:last-child { margin-bottom: 0; }
.obs-service-group__title {
	font-family: var(--obs-font-script);
	font-weight: 400;
	font-size: 2.3rem;
	line-height: 1;
	color: var(--obs-navy);
	border-bottom: 2px solid var(--obs-blue-light);
	/* 10px between the group name and its rule, and 10px between that rule
	   and the cards below. The rule is this heading's own border-bottom, so
	   padding controls the gap above it and margin the gap below. */
	padding-bottom: 10px;
	margin-bottom: 10px;
}
.obs-service-group__desc { color: var(--obs-ink-soft); margin-bottom: 24px; max-width: 640px; }

.obs-service-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 28px;
}
.obs-service-card {
	background: #fff;
	border-radius: var(--obs-radius-lg);
	overflow: hidden;
	box-shadow: var(--obs-shadow-soft);
	display: flex;
	flex-direction: column;
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.obs-service-card:hover { transform: translateY(-4px); box-shadow: var(--obs-shadow); }
.obs-service-card__media { aspect-ratio: 4 / 3; overflow: hidden; }
/* <picture> is inline by default, so the img's height:100% would have no
   resolved parent height and the 4:3 media box would collapse. */
.obs-service-card__media picture { display: block; width: 100%; height: 100%; }
.obs-service-card__img { width: 100%; height: 100%; object-fit: cover; }
.obs-service-card__body { padding: 22px 22px 24px; display: flex; flex-direction: column; flex: 1; }
.obs-service-card__title { font-family: var(--obs-font-display); font-size: 1.25rem; color: var(--obs-navy); margin-bottom: 8px; }
.obs-service-card__desc { font-size: 0.9rem; color: var(--obs-ink-soft); margin-bottom: 16px; flex-grow: 1; }

.obs-service-card__pricing { border-top: 1px solid var(--obs-line); padding-top: 12px; }
.obs-service-card__pricing li {
	display: flex; justify-content: space-between; align-items: baseline;
	padding: 5px 0; font-size: 0.88rem;
}
.obs-service-card__duration { color: var(--obs-ink-soft); }
.obs-service-card__price { font-weight: 700; color: var(--obs-navy); font-family: var(--obs-font-body); }

/* ---------------------------------------------------------------------
   Specials
--------------------------------------------------------------------- */
.obs-specials-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 28px;
}
.obs-special-card {
	background: #fff;
	border-radius: var(--obs-radius-lg);
	overflow: hidden;
	box-shadow: var(--obs-shadow-soft);
}
.obs-special-card__media { position: relative; aspect-ratio: 3 / 2; }
.obs-special-card__img { width: 100%; height: 100%; object-fit: cover; }
.obs-special-card__ribbon {
	position: absolute; top: 16px; left: 16px;
	background: var(--obs-navy); color: #fff;
	font-size: 0.7rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
	padding: 6px 14px; border-radius: 100px;
}
.obs-special-card__body { padding: 22px; }
.obs-special-card__body h3 { font-family: var(--obs-font-display); font-size: 1.3rem; color: var(--obs-navy); margin-bottom: 10px; }

/* ---------------------------------------------------------------------
   Gift cards
--------------------------------------------------------------------- */
.obs-giftcards { background: var(--obs-navy); color: #fff; }
.obs-giftcards__grid { display: grid; grid-template-columns: 0.9fr 1fr; gap: 72px; align-items: center; }
.obs-giftcards__image { border-radius: var(--obs-radius-lg); aspect-ratio: 9 / 7; box-shadow: var(--obs-shadow); }
/* <picture> is inline by default, which would collapse the media box. */
.obs-giftcards__media picture { display: block; }
.obs-giftcards__media img.obs-giftcards__image { width: 100%; height: 100%; object-fit: cover; display: block; }
.obs-giftcards .obs-eyebrow { color: var(--obs-blue-light); }
.obs-giftcards h2 { color: #fff; }
.obs-giftcards__copy p { color: rgba(255,255,255,0.82); }

.obs-check-list { margin: 22px 0 30px; display: flex; flex-direction: column; gap: 12px; }
.obs-check-list li { display: flex; align-items: center; gap: 12px; font-size: 0.95rem; }
.obs-check-list svg { width: 20px; height: 20px; color: var(--obs-blue-light); flex-shrink: 0; }

/* ---------------------------------------------------------------------
   Testimonials / trust badges
--------------------------------------------------------------------- */
.obs-trust-grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 24px;
}
.obs-trust-card {
	background: #fff;
	border-radius: var(--obs-radius-lg);
	padding: 32px 24px;
	text-align: center;
	box-shadow: var(--obs-shadow-soft);
}
.obs-trust-card svg { width: 34px; height: 34px; color: var(--obs-blue); margin-bottom: 14px; }
.obs-trust-card h4 { font-family: var(--obs-font-display); font-size: 1.1rem; color: var(--obs-navy); margin-bottom: 8px; }
.obs-trust-card p { font-size: 0.88rem; color: var(--obs-ink-soft); margin: 0; }
.obs-testimonials__note { text-align: center; margin-top: 32px; color: var(--obs-ink-soft); font-size: 0.85rem; font-style: italic; }

.obs-testimonial-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.obs-testimonial-card { background: #fff; border-radius: var(--obs-radius-lg); padding: 30px; box-shadow: var(--obs-shadow-soft); margin: 0; }
.obs-testimonial-card__stars { display: flex; gap: 3px; color: #E0A951; margin-bottom: 14px; }
.obs-testimonial-card__stars svg { width: 16px; height: 16px; }
.obs-testimonial-card p { font-size: 0.95rem; color: var(--obs-ink-soft); font-style: italic; }
.obs-testimonial-card cite { font-style: normal; font-weight: 700; color: var(--obs-navy); font-size: 0.85rem; }

/* ---------------------------------------------------------------------
   Contact
--------------------------------------------------------------------- */
.obs-contact__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 72px; align-items: start; }
.obs-contact__details { display: flex; flex-direction: column; gap: 22px; margin: 28px 0 32px; }
.obs-contact__details li { display: flex; gap: 16px; align-items: flex-start; }
.obs-contact__details svg { width: 22px; height: 22px; color: var(--obs-blue); flex-shrink: 0; margin-top: 2px; }
.obs-contact__details strong { display: block; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--obs-ink-soft); margin-bottom: 2px; }
.obs-contact__details span, .obs-contact__details a { font-size: 1rem; color: var(--obs-navy); font-weight: 600; }

.obs-contact__map { border-radius: var(--obs-radius-lg); overflow: hidden; box-shadow: var(--obs-shadow-soft); }
.obs-contact__map iframe { width: 100%; height: 320px; border: 0; display: block; }

.obs-contact__form-wrap {
	background: var(--obs-off-white);
	border-radius: var(--obs-radius-lg);
	padding: 40px;
}
.obs-contact__form-wrap h3 { font-family: var(--obs-font-display); font-size: 1.5rem; color: var(--obs-navy); margin-bottom: 8px; }
.obs-contact__form-wrap p { color: var(--obs-ink-soft); }

.obs-form__honeypot { position: absolute; left: -9999px; }
.obs-form__row { margin-bottom: 18px; display: flex; flex-direction: column; gap: 6px; }
.obs-form__row--split { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 0; }
.obs-form__row--split > div { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
.obs-form label { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--obs-navy); }
.obs-form input, .obs-form select, .obs-form textarea {
	border: 1px solid var(--obs-line);
	border-radius: var(--obs-radius);
	padding: 12px 14px;
	background: #fff;
	color: var(--obs-ink);
}
.obs-form input:focus, .obs-form select:focus, .obs-form textarea:focus {
	outline: none; border-color: var(--obs-blue); box-shadow: 0 0 0 3px rgba(118,149,193,0.25);
}
.obs-form textarea { resize: vertical; }
.obs-form button { margin-top: 6px; }

.obs-form-notice { padding: 14px 18px; border-radius: var(--obs-radius); margin-bottom: 20px; font-size: 0.9rem; font-weight: 600; }
.obs-form-notice--success { background: #E3F3EA; color: #1F6B3E; }
.obs-form-notice--error { background: #FBE7E5; color: #A32B21; }

/* ---------------------------------------------------------------------
   Footer
--------------------------------------------------------------------- */
.obs-footer { background: var(--obs-navy); color: rgba(255,255,255,0.85); }
.obs-footer__top {
	max-width: var(--obs-container);
	margin: 0 auto;
	padding: 72px 28px 48px;
	display: grid;
	grid-template-columns: 1.4fr 1fr 1fr 1fr;
	gap: 40px;
}
.obs-footer h4 { color: #fff; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 18px; }
.obs-footer__col p { font-size: 0.9rem; margin-bottom: 10px; }
.obs-footer__col a:hover { color: var(--obs-blue-light); }
/* Footer logo — sits above the tagline in the left brand column. Styled
   here rather than inheriting the header's .obs-logo rules, which are
   scoped to .obs-header__logo because the homepage crossfade makes them
   absolutely positioned and transparent. */
.obs-footer__logo {
	display: block;
	height: auto;
	width: 100%;
	max-width: 230px;
	margin: 0 0 4px;
}

.obs-footer__tagline { max-width: 260px; margin: 18px 0 20px; font-size: 0.9rem; }
.obs-footer__social { display: flex; gap: 14px; }
.obs-footer__social a { width: 36px; height: 36px; border-radius: 50%; border: 1px solid rgba(255,255,255,0.25); display: flex; align-items: center; justify-content: center; }
.obs-footer__social svg { width: 16px; height: 16px; }
.obs-footer-nav { display: flex; flex-direction: column; gap: 10px; }
.obs-footer-nav a { font-size: 0.9rem; }

.obs-footer__bottom {
	border-top: 1px solid rgba(255,255,255,0.12);
	padding: 22px 28px;
	max-width: var(--obs-container);
	margin: 0 auto;
	display: flex;
	justify-content: space-between;
	font-size: 0.78rem;
	color: rgba(255,255,255,0.55);
}
.obs-footer__bottom a { text-decoration: underline; }

/* ---------------------------------------------------------------------
   Simple content pages (404, generic page.php)
--------------------------------------------------------------------- */
.obs-main--simple { padding: calc(var(--obs-header-h) + 64px) 0 96px; }
.obs-simple-content { max-width: 760px; margin: 0 auto; }
.obs-simple-content__thumb { margin-bottom: 28px; border-radius: var(--obs-radius-lg); overflow: hidden; }
.obs-404 { text-align: center; }
.obs-404 .obs-btn { margin-top: 20px; }

/* =========================================================================
   Responsive
   ========================================================================= */
@media (max-width: 1080px) {
	.obs-service-grid, .obs-specials-grid { grid-template-columns: repeat(2, 1fr); }
	.obs-trust-grid { grid-template-columns: repeat(2, 1fr); }
	.obs-testimonial-grid { grid-template-columns: repeat(2, 1fr); }
	.obs-about__grid, .obs-giftcards__grid, .obs-contact__grid { gap: 48px; }
}

@media (max-width: 860px) {
	.obs-header__nav { display: none; }
	.obs-header__toggle { display: flex; }
	.obs-header__cta { display: none; }
	.obs-header__inner { grid-template-columns: auto 1fr auto; }
	/* Explicit placement — the logo precedes the toggle in the DOM, so with
	   the logo pinned to column 2, grid's default (sparse) auto-placement
	   refuses to move backwards to column 1 for the toggle and drops it onto
	   an implicit second row, below the header bar. Pinning both to row 1
	   keeps the hamburger inside the bar. */
	.obs-header__toggle { grid-column: 1; grid-row: 1; }
	.obs-header__logo { grid-column: 2; grid-row: 1; justify-self: center; }

	.obs-about__grid { grid-template-columns: 1fr; }
	.obs-about__badge { left: 16px; bottom: -18px; }

	.obs-giftcards__grid { grid-template-columns: 1fr; }
	.obs-giftcards__media { order: 2; }

	.obs-contact__grid { grid-template-columns: 1fr; }

	.obs-service-grid, .obs-specials-grid, .obs-trust-grid, .obs-testimonial-grid { grid-template-columns: 1fr 1fr; }

	.obs-footer__top { grid-template-columns: 1fr 1fr; }
	.obs-footer__bottom { flex-direction: column; gap: 8px; text-align: center; }
}

@media (max-width: 600px) {
	/* The compact wordmark is set to max-width:none so it never scales down on
	   desktop, but at phone widths its intrinsic width (~300px) exceeds the
	   centre grid column and it runs underneath the hamburger. Cap it to the
	   column and tighten the gutter so the two never collide. */
	/* minmax(0,…) rather than a bare 1fr: a 1fr track has an implicit `auto`
	   minimum, so an oversized logo image forces the centre track wider than
	   its share and shunts the whole grid over the hamburger. Flooring the
	   minimum at 0 lets the track stay put and the image scale down instead. */
	.obs-header__inner { gap: 14px; grid-template-columns: auto minmax(0, 1fr) auto; }
	/* justify-self:center (set at the 860px breakpoint) shrink-wraps this cell
	   to its content — and its content is absolutely positioned, so it
	   contributes no width and the cell collapses to 0. Stretching it to fill
	   the track gives the centred logo a real box to size against. */
	.obs-header__logo { min-width: 0; justify-self: stretch; }
	/* The link is the flex item. Left to shrink it collapses to 0px wide, so
	   the image's max-width:100% resolves against nothing and never caps.
	   Filling the track gives the percentage something real to measure. */
	.obs-header__logo-link {
		min-width: 0;
		width: 100%;
		display: flex;
		justify-content: center;
	}
	/* On the homepage the wordmark is absolutely positioned and centred
	   (body.home .obs-logo, which also sets max-width:none and outranks a
	   plain .obs-logo rule). Because it centres on the column rather than
	   sitting in flow, an oversized image spills equally to both sides and
	   lands on top of the hamburger. These selectors match that specificity
	   so the cap actually applies, on the homepage and inner pages alike. */
	body.home .obs-header__logo .obs-logo,
	.obs-header__logo .obs-logo {
		max-width: 100%;
		width: auto;
		height: auto;
		max-height: 56px;
	}
	body.home .obs-header.is-scrolled .obs-header__logo .obs-logo,
	.obs-header.is-scrolled .obs-header__logo .obs-logo {
		max-height: 46px;
	}
	/* The crest is near-square and sized by an explicit height at each
	   breakpoint (116px / 92px), so its width stays far inside the centred
	   logo box and needs no clamping. An earlier guard here forced
	   height:auto with a 176px cap, which overrode those breakpoint heights
	   and left the crest rendering at nearly double size on phones — making
	   the shrink on scroll a jump rather than a glide. max-width alone is
	   enough insurance. */
	body.home .obs-header__logo .obs-logo-full {
		max-width: 100%;
	}

	.obs-section { padding: 64px 0; }
	.obs-hero__script { font-size: clamp(2.4rem, 12vw, 3.4rem); }
	.obs-service-grid, .obs-specials-grid, .obs-trust-grid, .obs-testimonial-grid { grid-template-columns: 1fr; }
	.obs-form__row--split { grid-template-columns: 1fr; }
	.obs-footer__top { grid-template-columns: 1fr; padding: 56px 24px 32px; }
	.obs-contact__form-wrap { padding: 28px 22px; }
	.obs-container { padding: 0 20px; }
}

/* Respect reduced-motion preference: pause decorative hero video */
@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	.obs-hero__scroll-cue span { animation: none; }
}

/* ===================================================================
 * 15. SCROLL REVEAL MOTION
 * -------------------------------------------------------------------
 * Elements fade + rise gently as they scroll into view. Progressive
 * enhancement: the hiding rules are scoped to .obs-js, a class added
 * to <html> by main.js, so if JavaScript fails or is disabled the
 * content renders normally and stays fully visible.
 * =================================================================== */

.obs-js .obs-reveal {
	opacity: 0;
	transform: translateY(20px);
	transition:
		opacity 0.8s cubic-bezier(0.22, 0.61, 0.36, 1),
		transform 0.8s cubic-bezier(0.22, 0.61, 0.36, 1);
	transition-delay: var(--obs-reveal-delay, 0ms);
	will-change: opacity, transform;
}

/* Media panels drift in from the side for a little more presence. */
.obs-js .obs-reveal--left  { transform: translateX(-28px); }
.obs-js .obs-reveal--right { transform: translateX(28px); }

/* Cards lift slightly further so the stagger reads clearly. */
.obs-js .obs-reveal--card { transform: translateY(28px); }

.obs-js .obs-reveal.is-visible {
	opacity: 1;
	transform: none;
	will-change: auto;
}

/* -------------------------------------------------------------------
 * Hero entrance: plays once on load rather than on scroll, since the
 * hero is already in view when the page opens.
 * ----------------------------------------------------------------- */
.obs-js .obs-hero__eyebrow,
.obs-js .obs-hero__script,
.obs-js .obs-hero .obs-btn {
	opacity: 0;
	animation: obs-hero-in 1s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
.obs-js .obs-hero__eyebrow  { animation-delay: 0.25s; }
.obs-js .obs-hero__script   { animation-delay: 0.45s; }
.obs-js .obs-hero .obs-btn  { animation-delay: 0.80s; }

@keyframes obs-hero-in {
	from { opacity: 0; transform: translateY(24px); }
	to   { opacity: 1; transform: none; }
}

/* -------------------------------------------------------------------
 * Reduced motion: show everything immediately, no transforms at all.
 * ----------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
	.obs-js .obs-reveal,
	.obs-js .obs-reveal--left,
	.obs-js .obs-reveal--right,
	.obs-js .obs-reveal--card {
		opacity: 1;
		transform: none;
		transition: none;
	}
	.obs-js .obs-hero__eyebrow,
	.obs-js .obs-hero__script,
	.obs-js .obs-hero .obs-btn {
		opacity: 1;
		animation: none;
	}
}

/* ===================================================================
 * 16. MOBILE OVERFLOW GUARD
 * -------------------------------------------------------------------
 * Nothing should ever scroll sideways on a phone. The guard below is
 * the safety net; the rule after it removes the actual cause, since
 * the sideways reveal transform pushed media panels past the viewport
 * edge on narrow screens.
 * =================================================================== */

html,
body {
	max-width: 100%;
	overflow-x: hidden;
}

@media (max-width: 860px) {
	/* Drop the horizontal drift on small screens — a 28px sideways offset
	   is wider than the gutter, so it overflowed the viewport mid-reveal.
	   These panels fade up instead, matching the rest of the page. */
	.obs-js .obs-reveal--left,
	.obs-js .obs-reveal--right {
		transform: translateY(20px);
	}
	.obs-js .obs-reveal--left.is-visible,
	.obs-js .obs-reveal--right.is-visible {
		transform: none;
	}
}

/* ===================================================================
 * 17. SHORELINE SECTION TRANSITIONS
 * -------------------------------------------------------------------
 * Every background-colour change between sections is softened into a
 * shoreline instead of a hard horizontal rule: a crest layer sitting
 * slightly proud of a fill layer, so a thin wet-sand line reads along
 * the top of the wave.
 *
 * The shape is a CSS mask rather than a coloured SVG, so a single pair
 * of shapes serves every colour pairing on the page — the colours come
 * from custom properties and are overridden per variant below.
 * =================================================================== */

.obs-wave-top {
	position: relative;
	--obs-wave-fill: var(--obs-off-white);
	--obs-wave-crest: var(--obs-cream);
	--obs-wave-crest-opacity: 0.55;
	--obs-wave-height: 96px;
}

.obs-wave-top::before,
.obs-wave-top::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	/* Drawn above the section's own box, into the previous section's
	   bottom padding, so nothing shifts and nothing is clipped. */
	bottom: 100%;
	height: var(--obs-wave-height);
	pointer-events: none;
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	-webkit-mask-position: bottom center;
	mask-position: bottom center;
	-webkit-mask-size: 100% 100%;
	mask-size: 100% 100%;
}

/* Crest — the wet-sand line. Sits under the fill, peeking out above it. */
.obs-wave-top::before {
	background-color: var(--obs-wave-crest);
	opacity: var(--obs-wave-crest-opacity);
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%201440%2096%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20fill%3D%27%23000%27%20d%3D%27M0%2C96%20L0%2C44%20C160%2C18%20320%2C14%20486%2C34%20C660%2C55%20812%2C86%20986%2C74%20C1148%2C63%201290%2C30%201440%2C20%20L1440%2C96%20Z%27%2F%3E%3C%2Fsvg%3E");
	mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%201440%2096%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20fill%3D%27%23000%27%20d%3D%27M0%2C96%20L0%2C44%20C160%2C18%20320%2C14%20486%2C34%20C660%2C55%20812%2C86%20986%2C74%20C1148%2C63%201290%2C30%201440%2C20%20L1440%2C96%20Z%27%2F%3E%3C%2Fsvg%3E");
}

/* Fill — matches the section's own background so the seam disappears. */
.obs-wave-top::after {
	background-color: var(--obs-wave-fill);
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%201440%2096%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20fill%3D%27%23000%27%20d%3D%27M0%2C96%20L0%2C58%20C150%2C34%20316%2C30%20480%2C50%20C654%2C71%20806%2C98%20980%2C86%20C1144%2C75%201292%2C46%201440%2C36%20L1440%2C96%20Z%27%2F%3E%3C%2Fsvg%3E");
	mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%201440%2096%27%20preserveAspectRatio%3D%27none%27%3E%3Cpath%20fill%3D%27%23000%27%20d%3D%27M0%2C96%20L0%2C58%20C150%2C34%20316%2C30%20480%2C50%20C654%2C71%20806%2C98%20980%2C86%20C1144%2C75%201292%2C46%201440%2C36%20L1440%2C96%20Z%27%2F%3E%3C%2Fsvg%3E");
}

/* Colour variants, named for the section the wave belongs to. */
.obs-wave-top--white {
	--obs-wave-fill: var(--obs-white);
}
.obs-wave-top--navy {
	--obs-wave-fill: var(--obs-navy);
	--obs-wave-crest: var(--obs-blue);
	--obs-wave-crest-opacity: 0.7;
}

/* For a light section rising out of the navy one above it. The crest is
   seen against navy here, and cream at low opacity desaturates into a
   muddy grey against that — a pale blue reads as surf instead. */
.obs-wave-top--on-navy {
	--obs-wave-crest: var(--obs-blue-light);
	--obs-wave-crest-opacity: 0.9;
}

/* Mirror alternate waves so a long scroll does not repeat one silhouette. */
.obs-wave-top--flip::before,
.obs-wave-top--flip::after {
	transform: scaleX(-1);
}

/* -------------------------------------------------------------------
 * Clearance for the section ABOVE a wave. The wave is drawn upward into
 * that section's bottom padding, so without extra room its content ends
 * up sitting on the crest.  Height of the wave + 50px of real breathing
 * room.
 * ----------------------------------------------------------------- */
.obs-wave-above { padding-bottom: 146px; }

@media (max-width: 860px) {
	.obs-wave-top { --obs-wave-height: 60px; }
	.obs-wave-above { padding-bottom: 110px; }
}

@media (max-width: 600px) {
	.obs-wave-top { --obs-wave-height: 44px; }
	.obs-wave-above { padding-bottom: 94px; }
}

/* ===================================================================
 * 18. ACCESSIBILITY
 * -------------------------------------------------------------------
 * Keyboard focus and touch target sizing. The audit found links and
 * buttons with no visible focus ring (browsers drop the default outline
 * once colours are customised) and several phone/email links below the
 * 44px minimum recommended for touch.
 * =================================================================== */

/* Visible focus ring for keyboard users only — :focus-visible keeps it
   from appearing on mouse clicks. The offset lifts it clear of the
   element so it reads on both light and navy backgrounds. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
	outline: 3px solid var(--obs-blue-deep);
	outline-offset: 3px;
	border-radius: 3px;
}

/* On navy backgrounds the deep blue ring is too close in value to read,
   so switch to the light tint there. */
.obs-footer a:focus-visible,
.obs-giftcards a:focus-visible,
.obs-header a:focus-visible,
.obs-header button:focus-visible,
.obs-mobile-nav a:focus-visible,
.obs-hero a:focus-visible {
	outline-color: var(--obs-blue-light);
}

/* Touch targets. Rather than inflating the visible text, the tap area is
   extended with padding where there is room to do so without shifting
   the layout. */
@media (max-width: 860px) {
	.obs-contact__details a,
	.obs-footer__col a,
	.obs-mobile-nav__phone {
		display: inline-block;
		padding-block: 12px;
	}

	/* Category jump pills land at 42px — just under the threshold. */
	.obs-services__jump a {
		min-height: 44px;
		display: inline-flex;
		align-items: center;
	}

	/* The hamburger is a 34x26 icon; pad the hit area out to 44px square
	   while leaving the drawn bars untouched. */
	.obs-header__toggle {
		box-sizing: content-box;
		padding: 9px 5px;
		margin: -9px -5px;
	}
}

/* ===================================================================
 * 19. DESKTOP HEADER — NAV ALIGNS TO THE CREST AT PAGE TOP
 * -------------------------------------------------------------------
 * At the top of the homepage the illustrated crest hangs well below the
 * header bar (26px to 202px, centre 114px) while the nav sits centred in
 * the 96px bar (centre 48px), so the links float high above the logo.
 * Dropping the nav by the 66px difference lines it up with the crest;
 * on scroll it returns to 0 and rides up into the blue bar alongside the
 * wordmark. Same easing and duration as the logo crossfade so the whole
 * header resolves as one movement.
 * =================================================================== */

@media (min-width: 861px) {
	.obs-header__nav,
	.obs-header__cta {
		transition: transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
	}

	body.home .obs-header:not(.is-scrolled) .obs-header__nav,
	body.home .obs-header:not(.is-scrolled) .obs-header__cta {
		transform: translateY(66px);
	}
}

/* Anyone who has asked for reduced motion gets the end state with no
   travel — the nav is simply already in place. */
@media (min-width: 861px) and (prefers-reduced-motion: reduce) {
	.obs-header__nav,
	.obs-header__cta {
		transition: none;
	}
}

/* Contact: bottom-align the form with the map.
 * The grid is align-items:start, so the shorter form card sat 120px short
 * of the left column and the section ended on a ragged edge. Only the form
 * is re-aligned — the info list must stay top-aligned with the heading.
 * Scoped above 860px, below which the grid is a single column and the two
 * are stacked rather than side by side. */
@media (min-width: 861px) {
	.obs-contact__form-wrap { align-self: end; }
}

/* ===================================================================
 * 20. MOBILE HEADER CENTRING, SCROLL CUE, HEADING LEADING
 * =================================================================== */

@media (max-width: 860px) {
	/* The logo cell is a grid column sitting to the right of the hamburger
	   column, so centring within that column left the logo ~17px right of
	   true centre. Taking it out of flow and centring on the header itself
	   makes it optically centred against the viewport regardless of what
	   sits beside it. */
	.obs-header__inner { position: relative; }

	.obs-header__logo {
		position: absolute;
		/* An absolutely positioned grid item with an explicit grid-column
		   resolves left/right against that column's grid area, not the
		   container — so left:50% was centring it inside column 2, which sits
		   right of centre. Spanning every column makes the grid area the full
		   header width, so 50% is true centre. */
		grid-column: 1 / -1;
		left: 50%;
		transform: translateX(-50%);
		/* Centring means the logo grows equally in both directions, so on the
		   narrowest phones it can reach the hamburger. The third term keeps
		   clear of it. */
		width: min(62vw, 280px, calc(100vw - 150px));
		justify-self: auto;
	}
}

/* Scroll cue nudged up off the bottom edge. */
.obs-hero__scroll-cue { bottom: 40px; }

/* Extra leading between the two lines of the About heading — the display
   serif and the larger script beneath it were running close together. */
.obs-about .obs-h2 { line-height: 1.45; }
.obs-about .obs-script { line-height: 1.4; }

/* ===================================================================
 * 21. ABOUT CAROUSEL
 * -------------------------------------------------------------------
 * Crossfading photo rotator in the About media frame. Slides are
 * stacked and faded rather than slid, which avoids any horizontal
 * movement that could reintroduce overflow on small screens.
 * =================================================================== */

.obs-carousel {
	position: relative;
	overflow: hidden;
	/* Inherits the 4:3 frame and rounding from .obs-about__image. */
}

.obs-carousel__track {
	position: absolute;
	inset: 0;
}

.obs-carousel {
	--obs-carousel-fade: 0.9s;
}

.obs-carousel__slide {
	position: absolute;
	inset: 0;
	margin: 0;
	opacity: 0;
	/* Non-active slides must not be reachable by keyboard or screen reader,
	   but visibility is not an animatable property — it flips instantly. That
	   made the outgoing slide disappear the moment it lost .is-active, so its
	   opacity fade was never seen and the effect read as a fade-in from blank
	   rather than a crossfade. Delaying the flip until the fade finishes keeps
	   both images on screen together, while still hiding the slide afterwards. */
	visibility: hidden;
	transition:
		opacity var(--obs-carousel-fade) cubic-bezier(0.4, 0, 0.2, 1),
		visibility 0s linear var(--obs-carousel-fade);
}

.obs-carousel__slide.is-active {
	opacity: 1;
	visibility: visible;
	transition:
		opacity var(--obs-carousel-fade) cubic-bezier(0.4, 0, 0.2, 1),
		visibility 0s linear 0s;
}

.obs-carousel__slide picture,
.obs-carousel__slide img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Dots sit bottom-right, clear of the "Naples, Florida" badge at bottom-left. */
.obs-carousel__dots {
	position: absolute;
	right: 16px;
	bottom: 16px;
	display: flex;
	gap: 8px;
	z-index: 2;
	/* Ten slides make this row roughly 310px wide, which is most of the frame
	   on smaller screens. Wrapping keeps it inside the image no matter how
	   many slides the client ends up with. */
	flex-wrap: wrap;
	justify-content: flex-end;
	max-width: calc(100% - 32px);
	gap: 4px;
}

/*
 * The "Naples, Florida" badge is pinned to the bottom-left of the same frame.
 * With four slides the dot row was short enough to sit beside it; at ten they
 * collide, and which widths collide depends on the frame's width at each
 * breakpoint — so the row is lifted above the badge at every size rather than
 * patched breakpoint by breakpoint.
 */
.obs-carousel__dots {
	/*
	 * 36px is the lowest this can sit while still clearing the badge at every
	 * width — checked from 320px to 1440px. On the shorter 4:3 frame a bigger
	 * lift left the row floating a quarter of the way up the photo.
	 */
	bottom: 36px;
}

/* The button itself is the hit area (24px, meeting WCAG 2.5.8); the visible
   9px dot is drawn as a centred pseudo-element inside it. An earlier version
   used an oversized pseudo-element for the hit area, but without a positioned
   parent those areas overlapped and a dot swallowed its neighbour's clicks. */
.obs-carousel__dot {
	position: relative;
	width: 24px;
	height: 24px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

.obs-carousel__dot::before {
	content: "";
	position: absolute;
	left: 50%;
	top: 50%;
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: rgba(255, 255, 255, 0.6);
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
	transform: translate(-50%, -50%);
	transition: background 0.3s ease, transform 0.3s ease;
}

.obs-carousel__dot.is-active::before {
	background: #fff;
	transform: translate(-50%, -50%) scale(1.4);
}

@media (prefers-reduced-motion: reduce) {
	.obs-carousel__slide { transition: none; }
}

/* ===================================================================
 * 22. HERO — ENLARGED LOGO LOCKUP + REPOSITIONED COPY
 * -------------------------------------------------------------------
 * The homepage logo was 176px tall, which rendered the
 * "Relax · Renew · Restore" tagline at roughly 9px — present but
 * unreadable. Enlarging the lockup scales that line to a legible size;
 * the hero copy then moves clear of it.
 *
 * The hero is a centred flexbox, so a bigger logo would have overlapped
 * centred copy. Switching to top alignment with an explicit offset makes
 * the copy position deterministic at every viewport height instead of
 * drifting with the fold.
 * =================================================================== */

/*
 * Hero logo size and copy placement are driven by two custom properties, so
 * the band the copy centres in is always derived from where the logo actually
 * ends. Hard-coding the band separately let the two drift apart whenever a
 * breakpoint changed only one of them.
 *
 *   --obs-logo-top : the logo's offset from the top of the page
 *   --obs-logo-h   : the rendered logo height
 *
 * The band runs from (top + height) down to the top of the scroll cue.
 */
body.home {
	--obs-logo-top: 26px;
	--obs-logo-h: 320px;
	/* Exactly the space the cue occupies: 40px from the bottom, plus the
	   69px cue (label + gap + mouse) — 109px total, measured. */
	--obs-cue-reserve: 109px;
	/* Optical correction. Geometric centring measures the copy's bounding
	   box, but the script face carries tall ascenders and deep descenders,
	   so its box extends well past the visible letterforms and the text
	   reads low. The logo artwork itself has no meaningful padding (2-3px),
	   so this is genuinely an optical adjustment, not a geometry fix. */
	--obs-hero-copy-shift: -50px;
}

body.home .obs-header__logo .obs-logo-full {
	top: var(--obs-logo-top);
	/* Sized by max-height/max-width with auto dimensions rather than a fixed
	   height. The rebalanced lockup is 1.46:1 (it was near-square), so on
	   narrow screens the width cap was clamping width while height stayed
	   fixed — squashing the artwork. Letting both dimensions be auto keeps
	   the aspect and fits inside whichever constraint binds first. */
	width: auto;
	height: auto;
	max-height: var(--obs-logo-h);
	/* No max-width here: on desktop the logo cell is a grid item whose only
	   child is absolutely positioned, so it has no intrinsic width and
	   max-width:100% would resolve to zero and collapse the logo. The cap is
	   applied below, where the cell has an explicit width. */
}

.obs-hero { align-items: flex-start; }

.obs-hero__content {
	position: absolute;
	left: 0;
	right: 0;
	top: calc(var(--obs-logo-top) + var(--obs-logo-h));
	bottom: var(--obs-cue-reserve);
	display: flex;
	flex-direction: column;
	justify-content: center;
	margin: 0 auto;
	padding-top: 0;
	transform: translateY(var(--obs-hero-copy-shift));
}

/* The headline's 34px bottom margin is part of the centred box, which pushed
   the visible copy above true centre. It is the last element now that the CTA
   is gone, so the margin serves no purpose here. */
.obs-hero__content .obs-hero__script { margin-bottom: 0; }

@media (max-width: 1100px) {
	body.home { --obs-logo-h: 290px; }
}

@media (max-width: 860px) {
	body.home { --obs-logo-top: 20px; --obs-logo-h: 195px; }
	/* Below 860px the logo cell is absolutely positioned with an explicit
	   width, so a percentage cap is meaningful and keeps the artwork clear
	   of the hamburger. */
	body.home .obs-header__logo .obs-logo-full { max-width: 100%; }
}

@media (max-width: 600px) {
	body.home { --obs-logo-top: 16px; --obs-logo-h: 165px; --obs-hero-copy-shift: -34px; }
}

/* Short viewports (landscape phones, small laptops) cannot afford the full
   lockup — the band would collapse to nothing. */
/*
 * Short viewports (laptops at 1280x720 / 1366x768 as well as small phones).
 * Raised from 700px to 820px: with the script now set at line-height 1.4 the
 * copy block is taller, and at 720px tall the band was reaching up into the
 * logo. A smaller logo and a reduced optical shift keep them clear.
 */
@media (max-height: 820px) {
	body.home { --obs-logo-h: 240px; --obs-hero-copy-shift: -30px; }
}
@media (max-height: 700px) {
	body.home { --obs-logo-h: 150px; --obs-hero-copy-shift: -26px; }
}

/* Gift Cards sits on navy, and the shared primary-button hover darkens to
   navy-2 (rgb 31,53,80) — all but identical to the section background
   (rgb 22,40,60), so the button visually dissolved on hover. Against a dark
   section the hover has to go lighter, not darker. The brand light tint gives
   9.12:1 against both the navy text on it and the section behind it. */
.obs-giftcards .obs-btn--primary:hover,
.obs-giftcards .obs-btn--primary:focus-visible {
	background: var(--obs-blue-light);
	color: var(--obs-navy);
}

/* ==========================================================================
   22. Hiring band ("Always Hiring Talented People")
   Sits between the last content section and the navy footer. Kept quiet on
   purpose — it speaks to job seekers, not guests, so it should not compete
   with the booking calls to action above it.
   ========================================================================== */
.obs-hiring {
	background: var(--obs-cream);
	/* The footer's wave rises into the bottom of this band, so the lower
	   padding is deliberately much larger than the top — it is clearance for
	   the wave crest, not visual balance. */
	padding: 64px 0 132px;
	text-align: center;
}

.obs-hiring__inner {
	max-width: 720px;
}

.obs-hiring__title {
	font-family: var(--obs-font-display);
	font-size: clamp(1.6rem, 3vw, 2.2rem);
	line-height: 1.2;
	color: var(--obs-navy);
	margin: 0 0 12px;
}

.obs-hiring__text {
	color: var(--obs-ink-soft);
	margin: 0 auto 26px;
	max-width: 56ch;
}

.obs-hiring__contact {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 14px 34px;
}

.obs-hiring__link {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	min-height: 44px; /* touch target */
	padding: 4px 6px;
	color: var(--obs-navy);
	text-decoration: none;
	font-weight: 500;
	letter-spacing: 0.02em;
	border-bottom: 1px solid transparent;
	transition: color 0.25s ease, border-color 0.25s ease;
}

.obs-hiring__link svg {
	width: 20px;
	height: 20px;
	flex: 0 0 auto;
	color: var(--obs-blue);
}

.obs-hiring__link:hover,
.obs-hiring__link:focus-visible {
	color: var(--obs-blue);
	border-bottom-color: currentColor;
}

@media (max-width: 600px) {
	.obs-hiring { padding: 48px 0 104px; }
	.obs-hiring__contact { gap: 8px 20px; }
}

/* ==========================================================================
   23. Coming Soon splash
   Temporary holding page. Reuses the hero's ocean video and the full logo so
   it reads as the same brand the finished site will open with.
   ========================================================================== */
.obs-soon-body {
	margin: 0;
	background: var(--obs-navy);
	font-family: var(--obs-font-body);
	color: #fff;
}

.obs-soon {
	position: relative;
	min-height: 100svh; /* svh avoids the mobile URL-bar jump */
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	padding: 48px 20px;
	box-sizing: border-box;
}

.obs-soon__video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	z-index: 0;
}

.obs-soon__scrim {
	position: absolute;
	inset: 0;
	z-index: 1;
	/* Deep enough for white text over any frame of the footage. */
	background: linear-gradient(180deg, rgba(11, 26, 44, 0.55) 0%, rgba(11, 26, 44, 0.68) 100%);
}

.obs-soon__inner {
	position: relative;
	z-index: 2;
	width: 100%;
	max-width: 1100px;
	text-align: center;
	display: flex;
	flex-direction: column;
	align-items: center;
}

/* Same presence as the home page hero crest. */
.obs-soon__logo {
	width: auto;
	max-width: 100%;
	max-height: 320px;
	height: auto;
	margin: 0 auto 40px;
	display: block;
	filter: drop-shadow(0 6px 24px rgba(0, 0, 0, 0.35));
}

.obs-soon__copy {
	width: 100%;
}

/* Matches .obs-hero__eyebrow — Roboto, wide caps. */
.obs-soon__eyebrow {
	font-family: var(--obs-font-body);
	font-size: 0.85rem;
	letter-spacing: 0.28em;
	text-transform: uppercase;
	font-weight: 600;
	margin: 0 0 18px;
	opacity: 0.95;
	color: #fff;
}

/*
 * The headline sits in Brittany Signature, as the hero's script line does.
 * It is held to a single line on purpose, so the size is driven by viewport
 * width rather than a fixed scale — at 320px the vw term keeps it inside the
 * screen, and the max stops it outgrowing the logo on a wide monitor.
 */
.obs-soon__script {
	font-family: var(--obs-font-script);
	font-weight: 400;
	font-size: clamp(2.4rem, 8.4vw, 6rem);
	line-height: 1.35;
	white-space: nowrap;
	/* The script face has deep descenders; the email line needs clearance. */
	margin: 0 0 36px;
	color: #fff;
	text-shadow: 0 6px 30px rgba(0, 0, 0, 0.25);
}

.obs-soon__contact {
	margin: 0 0 30px;
	font-family: var(--obs-font-body);
	font-size: 1rem;
	letter-spacing: 0.02em;
}

.obs-soon__contact a {
	color: #fff;
	text-decoration: none;
	border-bottom: 1px solid rgba(255, 255, 255, 0.45);
	padding: 4px 2px;
	display: inline-block;
	min-height: 32px;
}

.obs-soon__contact a:hover,
.obs-soon__contact a:focus-visible {
	border-bottom-color: #fff;
}

/* --- staff login ---------------------------------------------------- */
.obs-soon__toggle {
	background: none;
	border: 0;
	color: rgba(255, 255, 255, 0.85);
	font-family: var(--obs-font-body);
	font-size: 0.82rem;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	cursor: pointer;
	padding: 12px 16px;
	min-height: 44px;
}

.obs-soon__toggle:hover,
.obs-soon__toggle:focus-visible {
	color: #fff;
}

.obs-soon__form {
	display: none;
	justify-content: center;
	flex-wrap: wrap;
	gap: 10px;
	margin-top: 12px;
}

.obs-soon__form.is-open {
	display: flex;
}

.obs-soon__form input[type="password"] {
	background: rgba(255, 255, 255, 0.12);
	border: 1px solid rgba(255, 255, 255, 0.4);
	border-radius: 8px;
	color: #fff;
	padding: 12px 16px;
	font-size: 1rem;
	min-height: 46px;
	min-width: 200px;
}

.obs-soon__form input[type="password"]::placeholder {
	color: rgba(255, 255, 255, 0.65);
}

.obs-soon__form input[type="password"]:focus-visible {
	outline: 3px solid rgba(255, 255, 255, 0.8);
	outline-offset: 2px;
}

.obs-soon__error {
	flex: 1 0 100%;
	margin: 6px 0 0;
	font-size: 0.9rem;
	color: #ffd9d4;
}

@media (prefers-reduced-motion: reduce) {
	.obs-soon__video { display: none; }
}

@media (max-width: 600px) {
	.obs-soon__logo { max-height: 180px; margin-bottom: 20px; }
	.obs-soon__contact { margin-bottom: 28px; }
}
