/*
Theme Name: Moment 28
Theme URI: https://moment-28.com
Author: LAPIS
Description: One page theme for Moment 28, a scent design studio for retail and hospitality. Fixed full bleed background video with a natively scrolling copy track. No page builder, no framework, no JavaScript.
Version: 1.0.0
Requires at least: 6.5
Requires PHP: 7.4
License: Proprietary
Text Domain: moment28
*/

/* ---------------------------------------------------------------------------
   Design source of truth
   Figma Mx0T9cSxjuISSGbNTK3N6p, node 1:135 "Home Page", 1728 x 969.

   Vertical rhythm is expressed in vh against the 969px Figma viewport, so the
   spacing between copy blocks holds its proportion at any window height.
   Each Figma pixel value is kept in a comment beside its vh conversion.
   Horizontal values stay in px because the type is a fixed 22px and the
   measure has to break in the same places the design does.
--------------------------------------------------------------------------- */

/* --- Safe areas --------------------------------------------------------- */

/* The document declares viewport-fit=cover so the video reaches the physical
   edges of a notched phone instead of sitting inside a letterboxed layout
   viewport. That hands the notch, the Dynamic Island and the home indicator
   back to us, so every edge measurement below adds the corresponding inset.
   Off a notched device these all resolve to 0 and nothing changes. */

:root {
	--sat: env(safe-area-inset-top, 0px);
	--sar: env(safe-area-inset-right, 0px);
	--sab: env(safe-area-inset-bottom, 0px);
	--sal: env(safe-area-inset-left, 0px);
}

/* --- Reset ------------------------------------------------------------- */

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

html,
body {
	margin: 0;
	padding: 0;
	/* Black is set here and again inline in <head> so no white frame can
	   paint before this stylesheet arrives. */
	background-color: #000;
}

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

/* --- Type -------------------------------------------------------------- */

@font-face {
	font-family: "Realm";
	src: url("assets/fonts/realm-light.woff2") format("woff2"),
	     url("assets/fonts/realm-light.woff") format("woff");
	font-weight: 300;
	font-style: normal;
	font-display: swap;
}

body {
	font-family: "Realm", "Helvetica Neue", Helvetica, Arial, sans-serif;
	font-weight: 300;
	font-size: 22px;      /* Figma 22 */
	line-height: 28px;    /* Figma 28 */
	color: #fff;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/* --- Fixed layer ------------------------------------------------------- */

.stage {
	position: fixed;
	top: 0;
	left: 0;
	width: 100vw;
	height: 100vh;
	overflow: hidden;
	background-color: #000;
	z-index: 0;
}

/* Poster sits under the video permanently. It covers the load window, a
   refused autoplay, Low Power Mode on iOS, and a video file that fails
   outright, without a line of JavaScript. */
.stage__poster,
.stage__video {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.stage__poster {
	background-position: center center;
	background-repeat: no-repeat;
	background-size: cover;
}

.stage__video { border: 0; }

/* Left edge scrim. Bright petals drift left through the frame and pass
   behind the copy column, so this holds the text off the highlights.
   Toggled from Customizer > Moment 28 > Background. */
.stage__scrim {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
	background-image: linear-gradient(
		to right,
		rgba(0, 0, 0, 0.72) 0%,
		rgba(0, 0, 0, 0.62) 22%,
		rgba(0, 0, 0, 0.45) 33%,
		rgba(0, 0, 0, 0.20) 40%,
		rgba(0, 0, 0, 0.00) 48%
	);
}

/* --- Header lockup ----------------------------------------------------- */

.lockup {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
	z-index: 2;
}

.lockup__wordmark {
	position: absolute;
	top: calc(27px + var(--sat));   /* Figma y 27 */
	left: calc(36px + var(--sal));  /* Figma x 36 */
	width: 180px;  /* Figma 180 x 34 */
	height: 34px;
}

/* Figma places the monogram at left calc(95% - 1.6px), which resolves to
   x 1640 at the 1728 frame width, ie. 38px from the right edge. Pinned to
   the right here so the lockup keeps its 36 / 38 margins on any window
   width instead of drifting inward as the viewport grows. */
.lockup__monogram {
	position: absolute;
	top: calc(22px + var(--sat));   /* Figma y 22 */
	right: calc(38px + var(--sar)); /* Figma x 1640 + 50 = 1690, 1728 - 1690 = 38 */
	width: 50px;   /* Figma 50 x 42 */
	height: 42px;
	object-fit: cover;
}

/* --- Scrolling layer --------------------------------------------------- */

.track {
	position: relative;
	z-index: 1;
	padding-left: calc(39px + var(--sal));  /* Figma x 39, all copy */
	padding-right: var(--sar);
	padding-bottom: var(--sab);
	padding-top: 47.058vh;                  /* Figma y 456 / 969 */
}

.copy {
	margin: 0;
	padding: 0;
}

.copy p { margin: 0; }

/* min-height holds each block's slot so the next block starts on the Figma
   rhythm. If someone lengthens a block in the editor past its slot it simply
   grows and pushes the rest down, which is the correct behaviour. */
.copy--intro   { max-width: 432px; min-height: 64.396vh; } /* Figma w 432, 1080 - 456 = 624 */
.copy--memory  { max-width: 586px; min-height: 64.396vh; } /* Figma w 586, 1704 - 1080 = 624 */
.copy--approach{ max-width: 586px; min-height: 63.364vh; } /* Figma w 586, 2318 - 1704 = 614 */
.copy--contact { max-width: 578px; min-height: 16.099vh; } /* Figma w 578, 2474 - 2318 = 156 */

.copy__link {
	color: inherit;
	text-decoration: underline;
	text-decoration-skip-ink: none;
	text-underline-position: from-font;
}

.copy__link:hover,
.copy__link:focus-visible { text-decoration-thickness: 2px; }

/* Scroll hint glyph, Figma y 505.34, ie. 21.34px below the first line box. */
.scroll-hint {
	width: 15.44px;    /* Figma 15.4375 */
	height: 21.95px;   /* Figma 21.9502 */
	margin-top: 21.34px;
}

/* --- Reduced motion ---------------------------------------------------- */

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

/* --- Focus ------------------------------------------------------------- */

:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

.screen-reader-text {
	position: absolute !important;
	width: 1px; height: 1px;
	overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
	white-space: nowrap;
}

/* ===========================================================================
   Mobile and intermediate widths

   There is no mobile frame in Figma yet, so every value below is derived
   rather than pulled. What is derived from what:

   - Type does not scale with the viewport, it scales with reading distance.
     22/28 at arm's length on a desktop reads as roughly 18/26 on a phone
     held close. Tablet sits between at 20/27.
   - Margins keep the desktop's ratio of left margin to wordmark width
     (36/180 = 0.20). Phone: 24/132 = 0.18.
   - The vertical rhythm stays in vh, so the pacing between blocks holds its
     Figma proportion at every screen size. Nothing about it is redefined.
   - The lockup scales as one unit so the wordmark and monogram keep their
     relationship.

   Replace any of it the moment a mobile frame exists.
   ======================================================================== */

/* --- Tablet and small laptop ------------------------------------------- */

@media (max-width: 1023px) {

	body { font-size: 20px; line-height: 27px; }

	.lockup__wordmark { top: calc(24px + var(--sat)); left: calc(32px + var(--sal)); width: 156px; height: 29px; }
	.lockup__monogram { top: calc(20px + var(--sat)); right: calc(33px + var(--sar)); width: 43px; height: 36px; }

	.track {
		padding-left: calc(32px + var(--sal));
		padding-right: calc(32px + var(--sar));
	}

	.copy--intro    { max-width: 380px; }
	.copy--memory,
	.copy--approach { max-width: 520px; }
	.copy--contact  { max-width: 514px; }

	.stage__scrim {
		background-image: linear-gradient(
			to right,
			rgba(0, 0, 0, 0.72) 0%,
			rgba(0, 0, 0, 0.62) 34%,
			rgba(0, 0, 0, 0.45) 48%,
			rgba(0, 0, 0, 0.20) 56%,
			rgba(0, 0, 0, 0.00) 64%
		);
	}
}

/* --- Phone -------------------------------------------------------------- */

@media (max-width: 699px) {

	body { font-size: 18px; line-height: 26px; }

	.lockup__wordmark { top: calc(20px + var(--sat)); left: calc(24px + var(--sal)); width: 132px; height: 25px; }
	.lockup__monogram { top: calc(16px + var(--sat)); right: calc(25px + var(--sar)); width: 37px; height: 31px; }

	.track {
		padding-left: calc(24px + var(--sal));
		padding-right: calc(24px + var(--sar));
	}

	.copy--intro,
	.copy--memory,
	.copy--approach,
	.copy--contact { max-width: none; }

	/* The copy now spans nearly the full frame, so a left edge gradient has
	   nothing to grade against. Measured across the whole loop on the left
	   portrait crop of the licensed clip, 42% holds white text above 4.5:1
	   across roughly 99.85% of the frame, 99.5% on the worst frame. */
	.stage__scrim {
		background-image: none;
		background-color: rgba(0, 0, 0, 0.42);
	}

	.scroll-hint { width: 13.5px; height: 19.2px; margin-top: 18px; }

	/* The contact block wraps to four lines on a phone where it is two on
	   desktop, which eats its slot: an iPhone SE was ending 3px above the
	   bottom of the document. Drop the slot and set the tail explicitly, so
	   the copy always finishes on the same amount of air the desktop gets
	   (10.3vh) with a floor for short screens. */
	.copy--contact { min-height: 0; }

	.track { padding-bottom: calc(max(12vh, 88px) + var(--sab)); }
}

/* --- Portrait framing --------------------------------------------------- */

/* Covering a 16:9 clip into a portrait viewport throws most of its width
   away: a phone sees about a quarter of the frame, a portrait iPad about
   two fifths. Centred, that quarter lands straight on the brightest part of
   this particular clip, which is also its busiest. Anchored left it lands on
   the darkest and best composed part: open black above, the flower mass
   rising from below, which is where the copy wants to sit.

   Keyed to aspect ratio rather than width, because a portrait tablet has the
   same problem as a phone and none of its dimensions.

   25% is chosen on composition, checked against measurement. The far left of
   this clip is empty black, so anchoring hard left is the most legible crop
   and also a dead frame. 40% and beyond fills the whole viewport with flowers
   and leaves the copy nowhere to sit. 25% keeps open black across the upper
   half where the copy lives and the flower mass rising through the lower,
   which is the desktop composition in portrait. It costs a heavier scrim,
   which is the trade worth making. */
@media (max-aspect-ratio: 5 / 4) {
	.stage__video { object-position: 25% center; }
	.stage__poster { background-position: 25% center; }
}

/* --- Short viewports, mostly phones in landscape ------------------------ */

@media (max-height: 480px) and (orientation: landscape) {
	/* The rhythm is in vh, so on a 390px tall window the first block would
	   sit 183px down with the rest crowded under it. Floor the slots in px
	   so the copy keeps room to breathe. */
	.track { padding-top: max(47.058vh, 150px); }

	.copy--intro    { min-height: max(64.396vh, 260px); }
	.copy--memory   { min-height: max(64.396vh, 260px); }
	.copy--approach { min-height: max(63.364vh, 255px); }
	.copy--contact  { min-height: max(16.099vh, 80px); }

	/* Same reasoning as the phone tail: 16vh of a 390px tall window is 63px,
	   which leaves the contact block almost touching the bottom edge. */
	.track { padding-bottom: calc(max(10vh, 56px) + var(--sab)); }
}

/* --- iOS viewport ------------------------------------------------------- */

/* Safari reports 100vh as the tall viewport, which is what a fixed backdrop
   wants: it stays filled while the toolbar retracts rather than opening a gap
   at the bottom. lvh states that explicitly where it is understood. */
@supports (height: 100lvh) {
	.stage { height: 100lvh; }
}

/* ===========================================================================
   Scroll behaviour

   Three modes, set in Customizer > Moment 28 > Scroll behaviour. All three are
   pure CSS. There is still no JavaScript on this page.

   The reference, mount-rides.com, runs fullPage.js: it intercepts the wheel,
   touch and keyboard, animates a transform on a wrapper, and snaps one
   section per gesture, on mobile as well as desktop. That gets the look at a
   real cost. Momentum, flings, the scrollbar, find-in-page, deep links and
   Reduce Motion all stop behaving the way the browser intends, and on iOS it
   fights the address bar.

   CSS scroll snap is the native version of the same idea. The browser does
   the snapping, so momentum, keyboard, scrollbar and accessibility settings
   keep working, and it costs nothing to load.
   ======================================================================== */

/* --- Snap: each block catches dead centre ------------------------------- */

/* The snap target is .copy__inner, which is only as tall as its text, so
   centring it centres the copy rather than the taller spacing block around
   it. scroll-snap-stop keeps a fast flick from skipping a block, which is
   the part of the fullPage.js feel worth keeping. */

.m28-scroll-snap { scroll-snap-type: y mandatory; }

.m28-scroll-snap .copy__inner {
	scroll-snap-align: center;
	scroll-snap-stop: always;
}

/* The first block sits at 47vh in the Figma layout, just above centre, so its
   snap point is the top of the page and it does not move. The last block is
   the opposite problem: without room beneath it the page runs out of scroll
   before the block reaches the middle, and it would rest low on the screen
   forever. Half a viewport of tail is what it takes to let it centre. */
.m28-scroll-snap .track { padding-bottom: calc(50vh + var(--sab)); }

/* --- Pin: each block holds at centre while you keep scrolling ----------- */

.m28-scroll-pin .copy__inner {
	position: sticky;
	top: 0;
	height: 100vh;
	display: flex;
	flex-direction: column;
	justify-content: center;
}

/* A sticky child only has room to travel if its parent is taller than the
   viewport, so the slots grow in this mode. The difference between the slot
   and 100vh is how long each block holds still. */
.m28-scroll-pin .copy--intro,
.m28-scroll-pin .copy--memory,
.m28-scroll-pin .copy--approach { min-height: 165vh; }
.m28-scroll-pin .copy--contact  { min-height: 130vh; }
.m28-scroll-pin .track { padding-top: 0; }

/* --- Reduced motion ----------------------------------------------------- */

/* Someone who has asked for less motion should not have the page deciding
   where their scroll stops. */
@media (prefers-reduced-motion: reduce) {
	.m28-scroll-snap { scroll-snap-type: none; }
}
